mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-04 06:46:09 +00:00
f
This commit is contained in:
@@ -91,6 +91,8 @@ public final class ExternalDataPackPipeline {
|
||||
private static final String IMPORT_PREFIX = "imports";
|
||||
private static final String LOCATE_MANIFEST_PATH = "cache/external-datapack-locate-manifest.json";
|
||||
private static final String OBJECT_LOCATE_MANIFEST_PATH = "cache/external-datapack-object-locate-manifest.json";
|
||||
private static final String SMARTBORE_STRUCTURE_MANIFEST_PATH = "cache/external-datapack-smartbore-manifest.json";
|
||||
private static final String SUPPRESSED_VANILLA_STRUCTURE_MANIFEST_PATH = "cache/external-datapack-suppressed-vanilla-structures.json";
|
||||
private static final int CONNECT_TIMEOUT_MS = 4000;
|
||||
private static final int READ_TIMEOUT_MS = 8000;
|
||||
private static final int IMPORT_PARALLELISM = Math.max(1, Math.min(8, Runtime.getRuntime().availableProcessors()));
|
||||
@@ -99,6 +101,8 @@ public final class ExternalDataPackPipeline {
|
||||
private static final Map<String, String> PACK_ENVIRONMENT_CACHE = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Set<String>> RESOLVED_LOCATE_STRUCTURES_BY_ID = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Set<String>> RESOLVED_LOCATE_STRUCTURES_BY_OBJECT_KEY = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Set<String>> RESOLVED_SMARTBORE_STRUCTURES_BY_ID = new ConcurrentHashMap<>();
|
||||
private static final Set<String> SUPPRESSED_VANILLA_STRUCTURE_KEYS = ConcurrentHashMap.newKeySet();
|
||||
private static final AtomicCache<KMap<Identifier, StructurePlacement>> VANILLA_STRUCTURE_PLACEMENTS = new AtomicCache<>();
|
||||
private static final BlockData AIR = B.getAir();
|
||||
|
||||
@@ -203,11 +207,48 @@ public final class ExternalDataPackPipeline {
|
||||
return Set.copyOf(structures);
|
||||
}
|
||||
|
||||
public static Set<String> snapshotSmartBoreStructureKeys() {
|
||||
if (RESOLVED_SMARTBORE_STRUCTURES_BY_ID.isEmpty()) {
|
||||
Map<String, Set<String>> manifest = readSmartBoreManifest();
|
||||
if (!manifest.isEmpty()) {
|
||||
RESOLVED_SMARTBORE_STRUCTURES_BY_ID.putAll(manifest);
|
||||
}
|
||||
}
|
||||
|
||||
LinkedHashSet<String> structures = new LinkedHashSet<>();
|
||||
for (Set<String> values : RESOLVED_SMARTBORE_STRUCTURES_BY_ID.values()) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String value : values) {
|
||||
String normalized = normalizeLocateStructure(value);
|
||||
if (!normalized.isBlank()) {
|
||||
structures.add(normalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Set.copyOf(structures);
|
||||
}
|
||||
|
||||
public static Set<String> snapshotSuppressedVanillaStructureKeys() {
|
||||
if (SUPPRESSED_VANILLA_STRUCTURE_KEYS.isEmpty()) {
|
||||
Set<String> manifest = readSuppressedVanillaStructureManifest();
|
||||
if (!manifest.isEmpty()) {
|
||||
SUPPRESSED_VANILLA_STRUCTURE_KEYS.addAll(manifest);
|
||||
}
|
||||
}
|
||||
return Set.copyOf(SUPPRESSED_VANILLA_STRUCTURE_KEYS);
|
||||
}
|
||||
|
||||
public static PipelineSummary processDatapacks(List<DatapackRequest> requests, Map<String, KList<File>> worldDatapackFoldersByPack) {
|
||||
PipelineSummary summary = new PipelineSummary();
|
||||
PACK_ENVIRONMENT_CACHE.clear();
|
||||
RESOLVED_LOCATE_STRUCTURES_BY_ID.clear();
|
||||
RESOLVED_LOCATE_STRUCTURES_BY_OBJECT_KEY.clear();
|
||||
RESOLVED_SMARTBORE_STRUCTURES_BY_ID.clear();
|
||||
SUPPRESSED_VANILLA_STRUCTURE_KEYS.clear();
|
||||
|
||||
Set<File> knownWorldDatapackFolders = new LinkedHashSet<>();
|
||||
if (worldDatapackFoldersByPack != null) {
|
||||
@@ -233,6 +274,8 @@ public final class ExternalDataPackPipeline {
|
||||
Iris.info("Downloading datapacks [0/0] Downloading/Done!");
|
||||
writeLocateManifest(Map.of());
|
||||
writeObjectLocateManifest(Map.of());
|
||||
writeSmartBoreManifest(Map.of());
|
||||
writeSuppressedVanillaStructureManifest(Set.of());
|
||||
summary.legacyWorldCopyRemovals += pruneManagedWorldDatapacks(knownWorldDatapackFolders, Set.of());
|
||||
return summary;
|
||||
}
|
||||
@@ -240,6 +283,8 @@ public final class ExternalDataPackPipeline {
|
||||
List<RequestedSourceInput> sourceInputs = new ArrayList<>();
|
||||
LinkedHashMap<String, Set<String>> resolvedLocateStructuresById = new LinkedHashMap<>();
|
||||
LinkedHashMap<String, Set<String>> resolvedLocateStructuresByObjectKey = new LinkedHashMap<>();
|
||||
LinkedHashMap<String, Set<String>> resolvedSmartBoreStructuresById = new LinkedHashMap<>();
|
||||
LinkedHashSet<String> suppressedVanillaStructures = new LinkedHashSet<>();
|
||||
for (int requestIndex = 0; requestIndex < normalizedRequests.size(); requestIndex++) {
|
||||
DatapackRequest request = normalizedRequests.get(requestIndex);
|
||||
if (request == null) {
|
||||
@@ -285,8 +330,12 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
writeLocateManifest(resolvedLocateStructuresById);
|
||||
writeObjectLocateManifest(resolvedLocateStructuresByObjectKey);
|
||||
writeSmartBoreManifest(resolvedSmartBoreStructuresById);
|
||||
writeSuppressedVanillaStructureManifest(suppressedVanillaStructures);
|
||||
RESOLVED_LOCATE_STRUCTURES_BY_ID.putAll(resolvedLocateStructuresById);
|
||||
RESOLVED_LOCATE_STRUCTURES_BY_OBJECT_KEY.putAll(resolvedLocateStructuresByObjectKey);
|
||||
RESOLVED_SMARTBORE_STRUCTURES_BY_ID.putAll(resolvedSmartBoreStructuresById);
|
||||
SUPPRESSED_VANILLA_STRUCTURE_KEYS.addAll(suppressedVanillaStructures);
|
||||
return summary;
|
||||
}
|
||||
|
||||
@@ -376,6 +425,13 @@ public final class ExternalDataPackPipeline {
|
||||
summary.worldDatapacksInstalled += projectionResult.installedDatapacks();
|
||||
summary.worldAssetsInstalled += projectionResult.installedAssets();
|
||||
mergeResolvedLocateStructures(resolvedLocateStructuresById, request.id(), projectionResult.resolvedLocateStructures());
|
||||
suppressedVanillaStructures.addAll(determineSuppressedVanillaStructures(request, projectionResult.projectedStructureKeys()));
|
||||
if (request.supportSmartBore()) {
|
||||
LinkedHashSet<String> smartBoreTargets = new LinkedHashSet<>();
|
||||
smartBoreTargets.addAll(request.resolvedLocateStructures());
|
||||
smartBoreTargets.addAll(projectionResult.resolvedLocateStructures());
|
||||
mergeResolvedLocateStructures(resolvedSmartBoreStructuresById, request.id(), smartBoreTargets);
|
||||
}
|
||||
LinkedHashSet<String> objectLocateTargets = new LinkedHashSet<>();
|
||||
objectLocateTargets.addAll(request.resolvedLocateStructures());
|
||||
objectLocateTargets.addAll(projectionResult.resolvedLocateStructures());
|
||||
@@ -425,8 +481,12 @@ public final class ExternalDataPackPipeline {
|
||||
|
||||
writeLocateManifest(resolvedLocateStructuresById);
|
||||
writeObjectLocateManifest(resolvedLocateStructuresByObjectKey);
|
||||
writeSmartBoreManifest(resolvedSmartBoreStructuresById);
|
||||
writeSuppressedVanillaStructureManifest(suppressedVanillaStructures);
|
||||
RESOLVED_LOCATE_STRUCTURES_BY_ID.putAll(resolvedLocateStructuresById);
|
||||
RESOLVED_LOCATE_STRUCTURES_BY_OBJECT_KEY.putAll(resolvedLocateStructuresByObjectKey);
|
||||
RESOLVED_SMARTBORE_STRUCTURES_BY_ID.putAll(resolvedSmartBoreStructuresById);
|
||||
SUPPRESSED_VANILLA_STRUCTURE_KEYS.addAll(suppressedVanillaStructures);
|
||||
return summary;
|
||||
}
|
||||
|
||||
@@ -438,6 +498,14 @@ public final class ExternalDataPackPipeline {
|
||||
return Iris.instance.getDataFile(OBJECT_LOCATE_MANIFEST_PATH);
|
||||
}
|
||||
|
||||
private static File getSmartBoreManifestFile() {
|
||||
return Iris.instance.getDataFile(SMARTBORE_STRUCTURE_MANIFEST_PATH);
|
||||
}
|
||||
|
||||
private static File getSuppressedVanillaStructureManifestFile() {
|
||||
return Iris.instance.getDataFile(SUPPRESSED_VANILLA_STRUCTURE_MANIFEST_PATH);
|
||||
}
|
||||
|
||||
private static String normalizeLocateId(String id) {
|
||||
if (id == null) {
|
||||
return "";
|
||||
@@ -464,6 +532,26 @@ public final class ExternalDataPackPipeline {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static Set<String> determineSuppressedVanillaStructures(DatapackRequest request, Set<String> projectedStructureKeys) {
|
||||
LinkedHashSet<String> suppressed = new LinkedHashSet<>();
|
||||
if (request == null || !request.replaceVanilla() || request.alongsideMode()) {
|
||||
return suppressed;
|
||||
}
|
||||
|
||||
Set<String> projected = projectedStructureKeys == null ? Set.of() : projectedStructureKeys;
|
||||
for (String structureTarget : request.structures()) {
|
||||
String normalizedTarget = normalizeLocateStructure(structureTarget);
|
||||
if (normalizedTarget.isBlank() || !normalizedTarget.startsWith("minecraft:")) {
|
||||
continue;
|
||||
}
|
||||
if (!projected.contains(normalizedTarget)) {
|
||||
suppressed.add(normalizedTarget);
|
||||
}
|
||||
}
|
||||
|
||||
return suppressed;
|
||||
}
|
||||
|
||||
private static String normalizeObjectLoadKey(String objectKey) {
|
||||
if (objectKey == null) {
|
||||
return "";
|
||||
@@ -678,6 +766,93 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeSmartBoreManifest(Map<String, Set<String>> resolvedSmartBoreStructuresById) {
|
||||
File output = getSmartBoreManifestFile();
|
||||
LinkedHashMap<String, Set<String>> normalized = new LinkedHashMap<>();
|
||||
if (resolvedSmartBoreStructuresById != null) {
|
||||
for (Map.Entry<String, Set<String>> entry : resolvedSmartBoreStructuresById.entrySet()) {
|
||||
String normalizedId = normalizeLocateId(entry.getKey());
|
||||
if (normalizedId.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LinkedHashSet<String> structures = new LinkedHashSet<>();
|
||||
Set<String> values = entry.getValue();
|
||||
if (values != null) {
|
||||
for (String structure : values) {
|
||||
String normalizedStructure = normalizeLocateStructure(structure);
|
||||
if (!normalizedStructure.isBlank()) {
|
||||
structures.add(normalizedStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!structures.isEmpty()) {
|
||||
normalized.put(normalizedId, Set.copyOf(structures));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject root = new JSONObject();
|
||||
root.put("generatedAt", Instant.now().toString());
|
||||
JSONObject mappings = new JSONObject();
|
||||
ArrayList<String> ids = new ArrayList<>(normalized.keySet());
|
||||
ids.sort(String::compareTo);
|
||||
for (String id : ids) {
|
||||
Set<String> structures = normalized.get(id);
|
||||
if (structures == null || structures.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ArrayList<String> sortedStructures = new ArrayList<>(structures);
|
||||
sortedStructures.sort(String::compareTo);
|
||||
JSONArray values = new JSONArray();
|
||||
for (String structure : sortedStructures) {
|
||||
values.put(structure);
|
||||
}
|
||||
mappings.put(id, values);
|
||||
}
|
||||
root.put("ids", mappings);
|
||||
|
||||
try {
|
||||
writeBytesToFile(root.toString(4).getBytes(StandardCharsets.UTF_8), output);
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("Failed to write external datapack smartbore manifest " + output.getPath());
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeSuppressedVanillaStructureManifest(Set<String> suppressedStructures) {
|
||||
File output = getSuppressedVanillaStructureManifestFile();
|
||||
LinkedHashSet<String> normalized = new LinkedHashSet<>();
|
||||
if (suppressedStructures != null) {
|
||||
for (String value : suppressedStructures) {
|
||||
String normalizedStructure = normalizeLocateStructure(value);
|
||||
if (normalizedStructure.isBlank() || !normalizedStructure.startsWith("minecraft:")) {
|
||||
continue;
|
||||
}
|
||||
normalized.add(normalizedStructure);
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject root = new JSONObject();
|
||||
root.put("generatedAt", Instant.now().toString());
|
||||
JSONArray values = new JSONArray();
|
||||
ArrayList<String> sorted = new ArrayList<>(normalized);
|
||||
sorted.sort(String::compareTo);
|
||||
for (String value : sorted) {
|
||||
values.put(value);
|
||||
}
|
||||
root.put("structures", values);
|
||||
|
||||
try {
|
||||
writeBytesToFile(root.toString(4).getBytes(StandardCharsets.UTF_8), output);
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("Failed to write external datapack suppressed-vanilla structure manifest " + output.getPath());
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Set<String>> readLocateManifest() {
|
||||
LinkedHashMap<String, Set<String>> mapped = new LinkedHashMap<>();
|
||||
File input = getLocateManifestFile();
|
||||
@@ -778,6 +953,88 @@ public final class ExternalDataPackPipeline {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
private static Map<String, Set<String>> readSmartBoreManifest() {
|
||||
LinkedHashMap<String, Set<String>> mapped = new LinkedHashMap<>();
|
||||
File input = getSmartBoreManifestFile();
|
||||
if (!input.exists() || !input.isFile()) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject root = new JSONObject(Files.readString(input.toPath(), StandardCharsets.UTF_8));
|
||||
JSONObject ids = root.optJSONObject("ids");
|
||||
if (ids == null) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
ArrayList<String> keys = new ArrayList<>(ids.keySet());
|
||||
keys.sort(String::compareTo);
|
||||
for (String key : keys) {
|
||||
String normalizedId = normalizeLocateId(key);
|
||||
if (normalizedId.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LinkedHashSet<String> structures = new LinkedHashSet<>();
|
||||
JSONArray values = ids.optJSONArray(key);
|
||||
if (values != null) {
|
||||
for (int i = 0; i < values.length(); i++) {
|
||||
Object rawValue = values.opt(i);
|
||||
if (rawValue == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String normalizedStructure = normalizeLocateStructure(String.valueOf(rawValue));
|
||||
if (!normalizedStructure.isBlank()) {
|
||||
structures.add(normalizedStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!structures.isEmpty()) {
|
||||
mapped.put(normalizedId, Set.copyOf(structures));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("Failed to read external datapack smartbore manifest " + input.getPath());
|
||||
Iris.reportError(e);
|
||||
}
|
||||
|
||||
return mapped;
|
||||
}
|
||||
|
||||
private static Set<String> readSuppressedVanillaStructureManifest() {
|
||||
LinkedHashSet<String> mapped = new LinkedHashSet<>();
|
||||
File input = getSuppressedVanillaStructureManifestFile();
|
||||
if (!input.exists() || !input.isFile()) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject root = new JSONObject(Files.readString(input.toPath(), StandardCharsets.UTF_8));
|
||||
JSONArray values = root.optJSONArray("structures");
|
||||
if (values == null) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
for (int i = 0; i < values.length(); i++) {
|
||||
Object rawValue = values.opt(i);
|
||||
if (rawValue == null) {
|
||||
continue;
|
||||
}
|
||||
String normalizedStructure = normalizeLocateStructure(String.valueOf(rawValue));
|
||||
if (!normalizedStructure.isBlank() && normalizedStructure.startsWith("minecraft:")) {
|
||||
mapped.add(normalizedStructure);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("Failed to read external datapack suppressed-vanilla structure manifest " + input.getPath());
|
||||
Iris.reportError(e);
|
||||
}
|
||||
|
||||
return mapped;
|
||||
}
|
||||
|
||||
private static List<DatapackRequest> normalizeRequests(List<DatapackRequest> requests) {
|
||||
Map<String, DatapackRequest> deduplicated = new HashMap<>();
|
||||
if (requests == null) {
|
||||
@@ -1028,7 +1285,10 @@ public final class ExternalDataPackPipeline {
|
||||
|
||||
String managedName = buildManagedWorldDatapackName(sourceDescriptor.targetPack(), sourceDescriptor.sourceKey());
|
||||
if (worldDatapackFolders == null || worldDatapackFolders.isEmpty()) {
|
||||
return ProjectionResult.success(managedName, 0, 0, Set.copyOf(request.resolvedLocateStructures()), 0);
|
||||
if (request.required()) {
|
||||
return ProjectionResult.failure(managedName, "no target world datapack folder is available for required external datapack request");
|
||||
}
|
||||
return ProjectionResult.success(managedName, 0, 0, Set.copyOf(request.resolvedLocateStructures()), 0, Set.of());
|
||||
}
|
||||
|
||||
ProjectionAssetSummary projectionAssetSummary;
|
||||
@@ -1041,7 +1301,14 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
|
||||
if (projectionAssetSummary.assets().isEmpty()) {
|
||||
return ProjectionResult.success(managedName, 0, 0, projectionAssetSummary.resolvedLocateStructures(), projectionAssetSummary.syntheticStructureSets());
|
||||
return ProjectionResult.success(
|
||||
managedName,
|
||||
0,
|
||||
0,
|
||||
projectionAssetSummary.resolvedLocateStructures(),
|
||||
projectionAssetSummary.syntheticStructureSets(),
|
||||
projectionAssetSummary.projectedStructureKeys()
|
||||
);
|
||||
}
|
||||
|
||||
int installedDatapacks = 0;
|
||||
@@ -1070,7 +1337,14 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
return ProjectionResult.success(managedName, installedDatapacks, installedAssets, projectionAssetSummary.resolvedLocateStructures(), projectionAssetSummary.syntheticStructureSets());
|
||||
return ProjectionResult.success(
|
||||
managedName,
|
||||
installedDatapacks,
|
||||
installedAssets,
|
||||
projectionAssetSummary.resolvedLocateStructures(),
|
||||
projectionAssetSummary.syntheticStructureSets(),
|
||||
projectionAssetSummary.projectedStructureKeys()
|
||||
);
|
||||
}
|
||||
|
||||
private static ProjectionAssetSummary buildProjectedAssets(File source, SourceDescriptor sourceDescriptor, DatapackRequest request) throws IOException {
|
||||
@@ -1081,7 +1355,16 @@ public final class ExternalDataPackPipeline {
|
||||
|
||||
List<ProjectionInputAsset> inputAssets = projectionSelection.assets();
|
||||
if (inputAssets.isEmpty()) {
|
||||
return new ProjectionAssetSummary(List.of(), Set.copyOf(request.resolvedLocateStructures()), 0);
|
||||
return new ProjectionAssetSummary(List.of(), Set.copyOf(request.resolvedLocateStructures()), 0, Set.of());
|
||||
}
|
||||
int selectedStructureNbtCount = 0;
|
||||
for (ProjectionInputAsset inputAsset : inputAssets) {
|
||||
if (inputAsset == null || inputAsset.entry() == null) {
|
||||
continue;
|
||||
}
|
||||
if (inputAsset.entry().type() == ProjectedEntryType.STRUCTURE_NBT) {
|
||||
selectedStructureNbtCount++;
|
||||
}
|
||||
}
|
||||
|
||||
String scopeNamespace = buildScopeNamespace(sourceDescriptor, request);
|
||||
@@ -1103,9 +1386,11 @@ public final class ExternalDataPackPipeline {
|
||||
LinkedHashSet<String> resolvedLocateStructures = new LinkedHashSet<>();
|
||||
resolvedLocateStructures.addAll(request.resolvedLocateStructures());
|
||||
LinkedHashSet<String> remappedStructureKeys = new LinkedHashSet<>();
|
||||
LinkedHashSet<String> projectedStructureKeys = new LinkedHashSet<>();
|
||||
LinkedHashSet<String> structureSetReferences = new LinkedHashSet<>();
|
||||
LinkedHashSet<String> writtenPaths = new LinkedHashSet<>();
|
||||
ArrayList<ProjectionOutputAsset> outputAssets = new ArrayList<>();
|
||||
int projectedCanonicalStructureNbtCount = 0;
|
||||
|
||||
for (ProjectionInputAsset inputAsset : inputAssets) {
|
||||
ProjectedEntry projectedEntry = inputAsset.entry();
|
||||
@@ -1152,6 +1437,10 @@ public final class ExternalDataPackPipeline {
|
||||
|
||||
remappedStructureKeys.add(effectiveEntry.key());
|
||||
resolvedLocateStructures.add(effectiveEntry.key());
|
||||
String normalizedProjectedStructure = normalizeLocateStructure(effectiveEntry.key());
|
||||
if (!normalizedProjectedStructure.isBlank()) {
|
||||
projectedStructureKeys.add(normalizedProjectedStructure);
|
||||
}
|
||||
} else if (projectedEntry.type() == ProjectedEntryType.STRUCTURE_SET) {
|
||||
structureSetReferences.addAll(readStructureSetReferences(root));
|
||||
}
|
||||
@@ -1160,6 +1449,11 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
|
||||
outputAssets.add(new ProjectionOutputAsset(outputRelativePath, outputBytes));
|
||||
if (projectedEntry.type() == ProjectedEntryType.STRUCTURE_NBT
|
||||
&& outputRelativePath.endsWith(".nbt")
|
||||
&& outputRelativePath.contains("/structure/")) {
|
||||
projectedCanonicalStructureNbtCount++;
|
||||
}
|
||||
}
|
||||
|
||||
int syntheticStructureSets = 0;
|
||||
@@ -1195,7 +1489,11 @@ public final class ExternalDataPackPipeline {
|
||||
outputAssets.add(new ProjectionOutputAsset(tagPath, root.toString(4).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
return new ProjectionAssetSummary(outputAssets, Set.copyOf(resolvedLocateStructures), syntheticStructureSets);
|
||||
if (request.required() && selectedStructureNbtCount > 0 && projectedCanonicalStructureNbtCount <= 0) {
|
||||
throw new IOException("Required external datapack projection produced no canonical structure template outputs (data/*/structure/*.nbt).");
|
||||
}
|
||||
|
||||
return new ProjectionAssetSummary(outputAssets, Set.copyOf(resolvedLocateStructures), syntheticStructureSets, Set.copyOf(projectedStructureKeys));
|
||||
}
|
||||
|
||||
private static ProjectionSelection readProjectedEntries(File source, DatapackRequest request) throws IOException {
|
||||
@@ -1775,7 +2073,7 @@ public final class ExternalDataPackPipeline {
|
||||
case TEMPLATE_POOL -> "data/" + namespace + "/worldgen/template_pool/" + path + ".json";
|
||||
case PROCESSOR_LIST -> "data/" + namespace + "/worldgen/processor_list/" + path + ".json";
|
||||
case BIOME_HAS_STRUCTURE_TAG -> "data/" + namespace + "/tags/worldgen/biome/has_structure/" + path + ".json";
|
||||
case STRUCTURE_NBT -> "data/" + namespace + "/structures/" + path + ".nbt";
|
||||
case STRUCTURE_NBT -> "data/" + namespace + "/structure/" + path + ".nbt";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3090,6 +3388,7 @@ public final class ExternalDataPackPipeline {
|
||||
String requiredEnvironment,
|
||||
boolean required,
|
||||
boolean replaceVanilla,
|
||||
boolean supportSmartBore,
|
||||
Set<String> structures,
|
||||
Set<String> structureSets,
|
||||
Set<String> configuredFeatures,
|
||||
@@ -3110,6 +3409,7 @@ public final class ExternalDataPackPipeline {
|
||||
String requiredEnvironment,
|
||||
boolean required,
|
||||
boolean replaceVanilla,
|
||||
boolean supportSmartBore,
|
||||
IrisExternalDatapackReplaceTargets replaceTargets,
|
||||
KList<IrisExternalDatapackStructurePatch> structurePatches
|
||||
) {
|
||||
@@ -3120,6 +3420,7 @@ public final class ExternalDataPackPipeline {
|
||||
requiredEnvironment,
|
||||
required,
|
||||
replaceVanilla,
|
||||
supportSmartBore,
|
||||
replaceTargets,
|
||||
structurePatches,
|
||||
Set.of(),
|
||||
@@ -3136,6 +3437,7 @@ public final class ExternalDataPackPipeline {
|
||||
String requiredEnvironment,
|
||||
boolean required,
|
||||
boolean replaceVanilla,
|
||||
boolean supportSmartBore,
|
||||
IrisExternalDatapackReplaceTargets replaceTargets,
|
||||
KList<IrisExternalDatapackStructurePatch> structurePatches,
|
||||
Set<String> forcedBiomeKeys,
|
||||
@@ -3150,6 +3452,7 @@ public final class ExternalDataPackPipeline {
|
||||
normalizeEnvironment(requiredEnvironment),
|
||||
required,
|
||||
replaceVanilla,
|
||||
supportSmartBore,
|
||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getStructures(), "worldgen/structure/"),
|
||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getStructureSets(), "worldgen/structure_set/"),
|
||||
normalizeTargets(replaceTargets == null ? null : replaceTargets.getConfiguredFeatures(), "worldgen/configured_feature/"),
|
||||
@@ -3216,6 +3519,7 @@ public final class ExternalDataPackPipeline {
|
||||
environment,
|
||||
required || other.required,
|
||||
replaceVanilla || other.replaceVanilla,
|
||||
supportSmartBore || other.supportSmartBore,
|
||||
union(structures, other.structures),
|
||||
union(structureSets, other.structureSets),
|
||||
union(configuredFeatures, other.configuredFeatures),
|
||||
@@ -3573,6 +3877,7 @@ public final class ExternalDataPackPipeline {
|
||||
int installedAssets,
|
||||
Set<String> resolvedLocateStructures,
|
||||
int syntheticStructureSets,
|
||||
Set<String> projectedStructureKeys,
|
||||
String managedName,
|
||||
String error
|
||||
) {
|
||||
@@ -3587,6 +3892,16 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
}
|
||||
resolvedLocateStructures = Set.copyOf(normalized);
|
||||
LinkedHashSet<String> normalizedProjected = new LinkedHashSet<>();
|
||||
if (projectedStructureKeys != null) {
|
||||
for (String structure : projectedStructureKeys) {
|
||||
String normalizedStructure = normalizeLocateStructure(structure);
|
||||
if (!normalizedStructure.isBlank()) {
|
||||
normalizedProjected.add(normalizedStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
projectedStructureKeys = Set.copyOf(normalizedProjected);
|
||||
syntheticStructureSets = Math.max(0, syntheticStructureSets);
|
||||
if (error == null) {
|
||||
error = "";
|
||||
@@ -3598,14 +3913,15 @@ public final class ExternalDataPackPipeline {
|
||||
int installedDatapacks,
|
||||
int installedAssets,
|
||||
Set<String> resolvedLocateStructures,
|
||||
int syntheticStructureSets
|
||||
int syntheticStructureSets,
|
||||
Set<String> projectedStructureKeys
|
||||
) {
|
||||
return new ProjectionResult(true, installedDatapacks, installedAssets, resolvedLocateStructures, syntheticStructureSets, managedName, "");
|
||||
return new ProjectionResult(true, installedDatapacks, installedAssets, resolvedLocateStructures, syntheticStructureSets, projectedStructureKeys, managedName, "");
|
||||
}
|
||||
|
||||
private static ProjectionResult failure(String managedName, String error) {
|
||||
String message = error == null || error.isBlank() ? "projection failed" : error;
|
||||
return new ProjectionResult(false, 0, 0, Set.of(), 0, managedName, message);
|
||||
return new ProjectionResult(false, 0, 0, Set.of(), 0, Set.of(), managedName, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3621,7 +3937,12 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
private record ProjectionAssetSummary(List<ProjectionOutputAsset> assets, Set<String> resolvedLocateStructures, int syntheticStructureSets) {
|
||||
private record ProjectionAssetSummary(
|
||||
List<ProjectionOutputAsset> assets,
|
||||
Set<String> resolvedLocateStructures,
|
||||
int syntheticStructureSets,
|
||||
Set<String> projectedStructureKeys
|
||||
) {
|
||||
private ProjectionAssetSummary {
|
||||
assets = assets == null ? List.of() : List.copyOf(assets);
|
||||
LinkedHashSet<String> normalized = new LinkedHashSet<>();
|
||||
@@ -3634,6 +3955,16 @@ public final class ExternalDataPackPipeline {
|
||||
}
|
||||
}
|
||||
resolvedLocateStructures = Set.copyOf(normalized);
|
||||
LinkedHashSet<String> normalizedProjected = new LinkedHashSet<>();
|
||||
if (projectedStructureKeys != null) {
|
||||
for (String structure : projectedStructureKeys) {
|
||||
String normalizedStructure = normalizeLocateStructure(structure);
|
||||
if (!normalizedStructure.isBlank()) {
|
||||
normalizedProjected.add(normalizedStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
projectedStructureKeys = Set.copyOf(normalizedProjected);
|
||||
syntheticStructureSets = Math.max(0, syntheticStructureSets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import art.arcane.iris.core.loader.ResourceLoader;
|
||||
import art.arcane.iris.core.nms.INMS;
|
||||
import art.arcane.iris.core.nms.datapack.DataVersion;
|
||||
import art.arcane.iris.core.nms.datapack.IDataFixer;
|
||||
import art.arcane.iris.engine.IrisNoisemapPrebakePipeline;
|
||||
import art.arcane.iris.engine.object.*;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
@@ -76,7 +77,10 @@ public class ServerConfigurator {
|
||||
}
|
||||
|
||||
deferredInstallPending = false;
|
||||
installDataPacks(true);
|
||||
boolean datapacksMissing = installDataPacks(true);
|
||||
if (!datapacksMissing) {
|
||||
IrisNoisemapPrebakePipeline.scheduleInstalledPacksPrebakeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static void configureIfDeferred() {
|
||||
@@ -135,13 +139,21 @@ public class ServerConfigurator {
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(boolean fullInstall, boolean includeExternal) {
|
||||
return installDataPacks(fullInstall, includeExternal, null);
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(
|
||||
boolean fullInstall,
|
||||
boolean includeExternal,
|
||||
KMap<String, KList<File>> extraWorldDatapackFoldersByPack
|
||||
) {
|
||||
IDataFixer fixer = DataVersion.getDefault();
|
||||
if (fixer == null) {
|
||||
DataVersion fallback = DataVersion.getLatest();
|
||||
Iris.warn("Primary datapack fixer was null, forcing latest fixer: " + fallback.getVersion());
|
||||
fixer = fallback.get();
|
||||
}
|
||||
return installDataPacks(fixer, fullInstall, includeExternal);
|
||||
return installDataPacks(fixer, fullInstall, includeExternal, extraWorldDatapackFoldersByPack);
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(IDataFixer fixer, boolean fullInstall) {
|
||||
@@ -149,6 +161,15 @@ public class ServerConfigurator {
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(IDataFixer fixer, boolean fullInstall, boolean includeExternal) {
|
||||
return installDataPacks(fixer, fullInstall, includeExternal, null);
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(
|
||||
IDataFixer fixer,
|
||||
boolean fullInstall,
|
||||
boolean includeExternal,
|
||||
KMap<String, KList<File>> extraWorldDatapackFoldersByPack
|
||||
) {
|
||||
if (fixer == null) {
|
||||
Iris.error("Unable to install datapacks, fixer is null!");
|
||||
return false;
|
||||
@@ -161,7 +182,7 @@ public class ServerConfigurator {
|
||||
DimensionHeight height = new DimensionHeight(fixer);
|
||||
KList<File> folders = getDatapacksFolder();
|
||||
if (includeExternal) {
|
||||
installExternalDataPacks(folders);
|
||||
installExternalDataPacks(folders, extraWorldDatapackFoldersByPack);
|
||||
}
|
||||
KMap<String, KSet<String>> biomes = new KMap<>();
|
||||
|
||||
@@ -184,13 +205,16 @@ public class ServerConfigurator {
|
||||
return fullInstall && verifyDataPacksPost(IrisSettings.get().getAutoConfiguration().isAutoRestartOnCustomBiomeInstall());
|
||||
}
|
||||
|
||||
private static void installExternalDataPacks(KList<File> folders) {
|
||||
private static void installExternalDataPacks(
|
||||
KList<File> folders,
|
||||
KMap<String, KList<File>> extraWorldDatapackFoldersByPack
|
||||
) {
|
||||
if (!IrisSettings.get().getGeneral().isImportExternalDatapacks()) {
|
||||
return;
|
||||
}
|
||||
|
||||
KList<ExternalDataPackPipeline.DatapackRequest> requests = collectExternalDatapackRequests();
|
||||
KMap<String, KList<File>> worldDatapackFoldersByPack = collectWorldDatapackFoldersByPack(folders);
|
||||
KMap<String, KList<File>> worldDatapackFoldersByPack = collectWorldDatapackFoldersByPack(folders, extraWorldDatapackFoldersByPack);
|
||||
ExternalDataPackPipeline.PipelineSummary summary = ExternalDataPackPipeline.processDatapacks(requests, worldDatapackFoldersByPack);
|
||||
if (summary.getLegacyDownloadRemovals() > 0) {
|
||||
Iris.verbose("Removed " + summary.getLegacyDownloadRemovals() + " legacy global datapack downloads.");
|
||||
@@ -315,6 +339,7 @@ public class ServerConfigurator {
|
||||
environment,
|
||||
definition.isRequired(),
|
||||
definition.isReplaceVanilla(),
|
||||
definition.isSupportSmartBore(),
|
||||
definition.getReplaceTargets(),
|
||||
definition.getStructurePatches(),
|
||||
Set.of(),
|
||||
@@ -343,6 +368,7 @@ public class ServerConfigurator {
|
||||
environment,
|
||||
group.required(),
|
||||
group.replaceVanilla(),
|
||||
definition.isSupportSmartBore(),
|
||||
definition.getReplaceTargets(),
|
||||
definition.getStructurePatches(),
|
||||
group.forcedBiomeKeys(),
|
||||
@@ -871,7 +897,10 @@ public class ServerConfigurator {
|
||||
) {
|
||||
}
|
||||
|
||||
private static KMap<String, KList<File>> collectWorldDatapackFoldersByPack(KList<File> fallbackFolders) {
|
||||
private static KMap<String, KList<File>> collectWorldDatapackFoldersByPack(
|
||||
KList<File> fallbackFolders,
|
||||
KMap<String, KList<File>> extraWorldDatapackFoldersByPack
|
||||
) {
|
||||
KMap<String, KList<File>> foldersByPack = new KMap<>();
|
||||
KMap<String, String> mappedWorlds = IrisWorlds.get().getWorlds();
|
||||
|
||||
@@ -905,6 +934,22 @@ public class ServerConfigurator {
|
||||
}
|
||||
}
|
||||
|
||||
if (extraWorldDatapackFoldersByPack != null && !extraWorldDatapackFoldersByPack.isEmpty()) {
|
||||
for (Map.Entry<String, KList<File>> entry : extraWorldDatapackFoldersByPack.entrySet()) {
|
||||
String packName = sanitizePackName(entry.getKey());
|
||||
if (packName.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
KList<File> folders = entry.getValue();
|
||||
if (folders == null || folders.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (File folder : folders) {
|
||||
addWorldDatapackFolder(foldersByPack, packName, folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foldersByPack;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ import art.arcane.volmlib.util.director.annotations.Director;
|
||||
import art.arcane.volmlib.util.director.annotations.Param;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
import art.arcane.volmlib.util.matter.MatterMarker;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.Material;
|
||||
@@ -161,7 +160,7 @@ public class CommandWhat implements DirectorExecutor {
|
||||
for (int zzz = c.getZ() - 4; zzz <= c.getZ() + 4; zzz++) {
|
||||
IrisToolbelt.access(c.getWorld()).getEngine().getMantle().findMarkers(xxx, zzz, new MatterMarker(marker))
|
||||
.convert((i) -> i.toLocation(c.getWorld())).forEach((i) -> {
|
||||
J.s(() -> BlockSignal.of(i.getBlock(), 100));
|
||||
BlockSignal.of(i.getWorld(), i.getBlockX(), i.getBlockY(), i.getBlockZ(), 100);
|
||||
v.incrementAndGet();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
|
||||
package art.arcane.iris.core.edit;
|
||||
|
||||
import art.arcane.iris.util.common.parallel.MultiBurst;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import art.arcane.volmlib.util.scheduling.SR;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.FallingBlock;
|
||||
@@ -46,26 +47,51 @@ public class BlockSignal {
|
||||
e.setSilent(true);
|
||||
e.setTicksLived(1);
|
||||
e.setVelocity(new Vector(0, 0, 0));
|
||||
J.s(() -> {
|
||||
e.remove();
|
||||
Location blockLocation = block.getLocation();
|
||||
Runnable removeTask = () -> {
|
||||
if (!J.runEntity(e, e::remove) && !e.isDead()) {
|
||||
e.remove();
|
||||
}
|
||||
active.decrementAndGet();
|
||||
BlockData type = block.getBlockData();
|
||||
MultiBurst.burst.lazy(() -> {
|
||||
for (Player i : block.getWorld().getPlayers()) {
|
||||
i.sendBlockChange(block.getLocation(), block.getBlockData());
|
||||
}
|
||||
});
|
||||
}, ticks);
|
||||
sendBlockRefresh(block);
|
||||
};
|
||||
if (!J.runAt(blockLocation, removeTask, ticks)) {
|
||||
if (!J.isFolia()) {
|
||||
J.s(removeTask, ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void of(Block block, int ticks) {
|
||||
new BlockSignal(block, ticks);
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
of(block.getWorld(), block.getX(), block.getY(), block.getZ(), ticks);
|
||||
}
|
||||
|
||||
public static void of(Block block) {
|
||||
of(block, 100);
|
||||
}
|
||||
|
||||
public static void of(World world, int x, int y, int z, int ticks) {
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = new Location(world, x, y, z);
|
||||
Runnable createTask = () -> new BlockSignal(world.getBlockAt(x, y, z), ticks);
|
||||
if (!J.runAt(location, createTask)) {
|
||||
if (!J.isFolia()) {
|
||||
J.s(createTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void of(World world, int x, int y, int z) {
|
||||
of(world, x, y, z, 100);
|
||||
}
|
||||
|
||||
public static Runnable forever(Block block) {
|
||||
Location tg = block.getLocation().clone().add(0.5, 0, 0.5).clone();
|
||||
FallingBlock e = block.getWorld().spawnFallingBlock(tg.clone(), block.getBlockData());
|
||||
@@ -82,26 +108,46 @@ public class BlockSignal {
|
||||
new SR(20) {
|
||||
@Override
|
||||
public void run() {
|
||||
if (e.isDead()) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
if (!J.runEntity(e, () -> {
|
||||
if (e.isDead()) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
e.setTicksLived(1);
|
||||
e.teleport(tg.clone());
|
||||
e.setVelocity(new Vector(0, 0, 0));
|
||||
e.setTicksLived(1);
|
||||
e.teleport(tg.clone());
|
||||
e.setVelocity(new Vector(0, 0, 0));
|
||||
})) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return () -> {
|
||||
e.remove();
|
||||
BlockData type = block.getBlockData();
|
||||
|
||||
MultiBurst.burst.lazy(() -> {
|
||||
for (Player i : block.getWorld().getPlayers()) {
|
||||
i.sendBlockChange(block.getLocation(), block.getBlockData());
|
||||
}
|
||||
});
|
||||
if (!J.runEntity(e, e::remove) && !e.isDead()) {
|
||||
e.remove();
|
||||
}
|
||||
Location blockLocation = block.getLocation();
|
||||
Runnable refreshTask = () -> sendBlockRefresh(block);
|
||||
if (!J.runAt(blockLocation, refreshTask)) {
|
||||
refreshTask.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void sendBlockRefresh(Block block) {
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location location = block.getLocation();
|
||||
BlockData blockData = block.getBlockData();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!player.getWorld().equals(location.getWorld())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
J.runEntity(player, () -> player.sendBlockChange(location, blockData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
@@ -48,8 +49,9 @@ public class DustRevealer {
|
||||
this.key = key;
|
||||
this.hits = hits;
|
||||
|
||||
J.s(() -> {
|
||||
new BlockSignal(world.getBlockAt(block.getX(), block.getY(), block.getZ()), 10);
|
||||
Location blockLocation = block.toBlock(world).getLocation();
|
||||
Runnable revealTask = () -> {
|
||||
BlockSignal.of(world, block.getX(), block.getY(), block.getZ(), 10);
|
||||
if (M.r(0.25)) {
|
||||
world.playSound(block.toBlock(world).getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 1f, RNG.r.f(0.2f, 2f));
|
||||
}
|
||||
@@ -90,7 +92,13 @@ public class DustRevealer {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}, RNG.r.i(2, 8));
|
||||
};
|
||||
int delay = RNG.r.i(2, 8);
|
||||
if (!J.runAt(blockLocation, revealTask, delay)) {
|
||||
if (!J.isFolia()) {
|
||||
J.s(revealTask, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void spawn(Block block, VolmitSender sender) {
|
||||
|
||||
@@ -33,6 +33,8 @@ import art.arcane.iris.engine.IrisNoisemapPrebakePipeline;
|
||||
import art.arcane.iris.engine.framework.SeedManager;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.volmlib.util.exceptions.IrisException;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
import art.arcane.volmlib.util.format.Form;
|
||||
@@ -166,8 +168,16 @@ public class IrisCreator {
|
||||
IrisWorlds.get().put(name(), dimension());
|
||||
}
|
||||
boolean verifyDataPacks = !studio();
|
||||
boolean includeExternalDataPacks = !studio();
|
||||
if (ServerConfigurator.installDataPacks(verifyDataPacks, includeExternalDataPacks)) {
|
||||
boolean includeExternalDataPacks = true;
|
||||
KMap<String, KList<File>> extraWorldDatapackFoldersByPack = null;
|
||||
if (studio()) {
|
||||
File studioDatapackFolder = new File(new File(Bukkit.getWorldContainer(), name()), "datapacks");
|
||||
KList<File> studioDatapackFolders = new KList<>();
|
||||
studioDatapackFolders.add(studioDatapackFolder);
|
||||
extraWorldDatapackFoldersByPack = new KMap<>();
|
||||
extraWorldDatapackFoldersByPack.put(d.getLoadKey(), studioDatapackFolders);
|
||||
}
|
||||
if (ServerConfigurator.installDataPacks(verifyDataPacks, includeExternalDataPacks, extraWorldDatapackFoldersByPack)) {
|
||||
throw new IrisException("Datapacks were missing!");
|
||||
}
|
||||
|
||||
@@ -295,6 +305,13 @@ public class IrisCreator {
|
||||
return;
|
||||
}
|
||||
|
||||
if (studio() && !benchmark) {
|
||||
boolean startupPrebakeReady = IrisNoisemapPrebakePipeline.awaitInstalledPacksPrebakeForStudio();
|
||||
if (startupPrebakeReady) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File targetDataFolder = new File(Bukkit.getWorldContainer(), name());
|
||||
if (studio() && !benchmark) {
|
||||
|
||||
@@ -38,6 +38,8 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -45,7 +47,9 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public final class IrisNoisemapPrebakePipeline {
|
||||
@@ -53,7 +57,10 @@ public final class IrisNoisemapPrebakePipeline {
|
||||
private static final long STARTUP_PROGRESS_INTERVAL_MS = Long.getLong("iris.prebake.progress.interval", 30000L);
|
||||
private static final int STATE_VERSION = 1;
|
||||
private static final String STATE_FILE = "noisemap-prebake.state";
|
||||
private static final AtomicBoolean STARTUP_PREBAKE_SCHEDULED = new AtomicBoolean(false);
|
||||
private static final AtomicBoolean STARTUP_PREBAKE_FAILURE_REPORTED = new AtomicBoolean(false);
|
||||
private static final AtomicInteger STARTUP_WORKER_SEQUENCE = new AtomicInteger();
|
||||
private static final AtomicReference<CompletableFuture<Void>> STARTUP_PREBAKE_COMPLETION = new AtomicReference<>();
|
||||
private static final ConcurrentHashMap<Class<?>, Field[]> FIELD_CACHE = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, Boolean> SKIP_ONCE = new ConcurrentHashMap<>();
|
||||
private static final Set<String> PREBAKE_LOADERS = Set.of(
|
||||
@@ -83,9 +90,64 @@ public final class IrisNoisemapPrebakePipeline {
|
||||
private IrisNoisemapPrebakePipeline() {
|
||||
}
|
||||
|
||||
public static void prebakeInstalledPacksAtStartup() {
|
||||
IrisSettings.IrisSettingsPregen settings = IrisSettings.get().getPregen();
|
||||
public static void scheduleInstalledPacksPrebakeAsync() {
|
||||
if (!IrisSettings.get().getPregen().isStartupNoisemapPrebake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!STARTUP_PREBAKE_SCHEDULED.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompletableFuture<Void> completion = new CompletableFuture<>();
|
||||
STARTUP_PREBAKE_COMPLETION.set(completion);
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
prebakeInstalledPacksAtStartup();
|
||||
completion.complete(null);
|
||||
} catch (Throwable throwable) {
|
||||
completion.completeExceptionally(throwable);
|
||||
if (STARTUP_PREBAKE_FAILURE_REPORTED.compareAndSet(false, true)) {
|
||||
Iris.warn("Startup noisemap pre-bake failed.");
|
||||
Iris.reportError(throwable);
|
||||
}
|
||||
}
|
||||
}, "Iris-StartupNoisemapPrebake");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public static boolean awaitInstalledPacksPrebakeForStudio() {
|
||||
if (!IrisSettings.get().getPregen().isStartupNoisemapPrebake()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scheduleInstalledPacksPrebakeAsync();
|
||||
CompletableFuture<Void> completion = STARTUP_PREBAKE_COMPLETION.get();
|
||||
if (completion == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
completion.join();
|
||||
return true;
|
||||
} catch (CompletionException e) {
|
||||
Throwable cause = e.getCause() == null ? e : e.getCause();
|
||||
if (STARTUP_PREBAKE_FAILURE_REPORTED.compareAndSet(false, true)) {
|
||||
Iris.warn("Startup noisemap pre-bake failed.");
|
||||
Iris.reportError(cause);
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable throwable) {
|
||||
if (STARTUP_PREBAKE_FAILURE_REPORTED.compareAndSet(false, true)) {
|
||||
Iris.warn("Startup noisemap pre-bake failed.");
|
||||
Iris.reportError(throwable);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void prebakeInstalledPacksAtStartup() {
|
||||
List<PrebakeTarget> targets = collectStartupTargets();
|
||||
if (targets.isEmpty()) {
|
||||
Iris.info("Startup noisemap pre-bake skipped (no installed or self-contained packs found).");
|
||||
|
||||
@@ -213,26 +213,24 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
J.s(() -> {
|
||||
for (Player player : world.getPlayers()) {
|
||||
if (player == null || !player.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
J.runEntity(player, () -> {
|
||||
int centerX = player.getLocation().getBlockX() >> 4;
|
||||
int centerZ = player.getLocation().getBlockZ() >> 4;
|
||||
int radius = 1;
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
int chunkX = centerX + x;
|
||||
int chunkZ = centerZ + z;
|
||||
raiseDiscoveredChunkFlag(world, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (Player player : getEngine().getWorld().getPlayers()) {
|
||||
if (player == null || !player.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
J.runEntity(player, () -> {
|
||||
int centerX = player.getLocation().getBlockX() >> 4;
|
||||
int centerZ = player.getLocation().getBlockZ() >> 4;
|
||||
int radius = 1;
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
int chunkX = centerX + x;
|
||||
int chunkZ = centerZ + z;
|
||||
raiseDiscoveredChunkFlag(world, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void raiseDiscoveredChunkFlag(World world, int chunkX, int chunkZ) {
|
||||
@@ -274,27 +272,25 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
J.s(() -> {
|
||||
for (Player player : world.getPlayers()) {
|
||||
if (player == null || !player.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
J.runEntity(player, () -> {
|
||||
int centerX = player.getLocation().getBlockX() >> 4;
|
||||
int centerZ = player.getLocation().getBlockZ() >> 4;
|
||||
int radius = 1;
|
||||
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
int targetX = centerX + x;
|
||||
int targetZ = centerZ + z;
|
||||
J.runRegion(world, targetX, targetZ, () -> updateChunkRegion(world, targetX, targetZ));
|
||||
}
|
||||
}
|
||||
});
|
||||
for (Player player : getEngine().getWorld().getPlayers()) {
|
||||
if (player == null || !player.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
J.runEntity(player, () -> {
|
||||
int centerX = player.getLocation().getBlockX() >> 4;
|
||||
int centerZ = player.getLocation().getBlockZ() >> 4;
|
||||
int radius = 1;
|
||||
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
int targetX = centerX + x;
|
||||
int targetZ = centerZ + z;
|
||||
J.runRegion(world, targetX, targetZ, () -> updateChunkRegion(world, targetX, targetZ));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChunkRegion(World world, int chunkX, int chunkZ) {
|
||||
@@ -432,7 +428,22 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
if (cl.flip()) {
|
||||
try {
|
||||
J.s(() -> precount = getEngine().getWorld().realWorld().getEntities());
|
||||
World realWorld = getEngine().getWorld().realWorld();
|
||||
if (realWorld == null) {
|
||||
precount = new KList<>();
|
||||
} else if (J.isFolia()) {
|
||||
precount = getFoliaEntitySnapshot(realWorld);
|
||||
} else {
|
||||
CompletableFuture<List<Entity>> future = new CompletableFuture<>();
|
||||
J.s(() -> {
|
||||
try {
|
||||
future.complete(realWorld.getEntities());
|
||||
} catch (Throwable ex) {
|
||||
future.completeExceptionally(ex);
|
||||
}
|
||||
});
|
||||
precount = future.get(2, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
close();
|
||||
}
|
||||
@@ -499,6 +510,47 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
private List<Entity> getFoliaEntitySnapshot(World world) {
|
||||
Map<String, Entity> snapshot = new ConcurrentHashMap<>();
|
||||
List<Player> players = getEngine().getWorld().getPlayers();
|
||||
if (players == null || players.isEmpty()) {
|
||||
return new KList<>();
|
||||
}
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(players.size());
|
||||
for (Player player : players) {
|
||||
if (player == null || !player.isOnline() || !world.equals(player.getWorld())) {
|
||||
latch.countDown();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!J.runEntity(player, () -> {
|
||||
try {
|
||||
snapshot.put(player.getUniqueId().toString(), player);
|
||||
for (Entity nearby : player.getNearbyEntities(64, 64, 64)) {
|
||||
if (nearby != null && world.equals(nearby.getWorld())) {
|
||||
snapshot.put(nearby.getUniqueId().toString(), nearby);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
})) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
latch.await(2, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
KList<Entity> entities = new KList<>();
|
||||
entities.addAll(snapshot.values());
|
||||
return entities;
|
||||
}
|
||||
|
||||
private void spawnChunkSafely(World world, int chunkX, int chunkZ, boolean initial) {
|
||||
if (world == null) {
|
||||
return;
|
||||
@@ -756,7 +808,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
public void teleportAsync(PlayerTeleportEvent e) {
|
||||
if (IrisSettings.get().getWorld().getAsyncTeleport().isEnabled()) {
|
||||
e.setCancelled(true);
|
||||
warmupAreaAsync(e.getPlayer(), e.getTo(), () -> J.s(() -> {
|
||||
warmupAreaAsync(e.getPlayer(), e.getTo(), () -> J.runEntity(e.getPlayer(), () -> {
|
||||
ignoreTP.set(true);
|
||||
e.getPlayer().teleport(e.getTo(), e.getCause());
|
||||
ignoreTP.set(false);
|
||||
@@ -1018,7 +1070,13 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
if (d.isNotEmpty()) {
|
||||
World w = e.getBlock().getWorld();
|
||||
J.s(() -> d.forEach(item -> w.dropItemNaturally(e.getBlock().getLocation().clone().add(.5, .5, .5), item)));
|
||||
Location dropLocation = e.getBlock().getLocation().clone().add(.5, .5, .5);
|
||||
Runnable dropTask = () -> d.forEach(item -> w.dropItemNaturally(dropLocation, item));
|
||||
if (!J.runAt(dropLocation, dropTask)) {
|
||||
if (!J.isFolia()) {
|
||||
J.s(dropTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package art.arcane.iris.engine.framework;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.nms.container.BlockPos;
|
||||
import art.arcane.iris.core.nms.container.Pair;
|
||||
@@ -38,11 +39,14 @@ import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import art.arcane.iris.util.common.scheduling.jobs.SingleJob;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -106,7 +110,7 @@ public interface Locator<T> {
|
||||
default void find(Player player, boolean teleport, String message) {
|
||||
find(player, location -> {
|
||||
if (teleport) {
|
||||
J.s(() -> player.teleport(location));
|
||||
J.runEntity(player, () -> teleportAsyncSafely(player, location));
|
||||
} else {
|
||||
player.sendMessage(C.GREEN + message + " at: " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ());
|
||||
}
|
||||
@@ -206,4 +210,39 @@ public interface Locator<T> {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
static void teleportAsyncSafely(Player player, Location location) {
|
||||
if (player == null || location == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (invokeNativeTeleportAsync(player, location)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
CompletableFuture<Boolean> teleportFuture = PaperLib.teleportAsync(player, location);
|
||||
if (teleportFuture != null) {
|
||||
teleportFuture.exceptionally(throwable -> {
|
||||
Iris.reportError(throwable);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
Iris.reportError(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean invokeNativeTeleportAsync(Player player, Location location) {
|
||||
try {
|
||||
Method teleportAsyncMethod = player.getClass().getMethod("teleportAsync", Location.class);
|
||||
teleportAsyncMethod.invoke(player, location);
|
||||
return true;
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
return false;
|
||||
} catch (Throwable throwable) {
|
||||
Iris.reportError(throwable);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.Arrays;
|
||||
|
||||
public class IrisCaveCarver3D {
|
||||
private static final byte LIQUID_AIR = 0;
|
||||
private static final byte LIQUID_WATER = 1;
|
||||
private static final byte LIQUID_LAVA = 2;
|
||||
private static final byte LIQUID_FORCED_AIR = 3;
|
||||
|
||||
@@ -48,7 +47,6 @@ public class IrisCaveCarver3D {
|
||||
private final KList<ModuleState> modules;
|
||||
private final double normalization;
|
||||
private final MatterCavern carveAir;
|
||||
private final MatterCavern carveWater;
|
||||
private final MatterCavern carveLava;
|
||||
private final MatterCavern carveForcedAir;
|
||||
|
||||
@@ -57,7 +55,6 @@ public class IrisCaveCarver3D {
|
||||
this.data = engine.getData();
|
||||
this.profile = profile;
|
||||
this.carveAir = new MatterCavern(true, "", LIQUID_AIR);
|
||||
this.carveWater = new MatterCavern(true, "", LIQUID_WATER);
|
||||
this.carveLava = new MatterCavern(true, "", LIQUID_LAVA);
|
||||
this.carveForcedAir = new MatterCavern(true, "", LIQUID_FORCED_AIR);
|
||||
this.modules = new KList<>();
|
||||
@@ -352,22 +349,7 @@ public class IrisCaveCarver3D {
|
||||
}
|
||||
|
||||
if (profile.isAllowWater() && y <= fluidHeight) {
|
||||
if (surfaceY - y < waterMinDepthBelowSurface) {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
double depthFactor = Math.max(0, Math.min(1.5, (fluidHeight - y) / 48D));
|
||||
double cutoff = 0.35 + (depthFactor * 0.2);
|
||||
double aquifer = signed(detailDensity.noise(x, y * 0.5D, z));
|
||||
if (aquifer <= cutoff) {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
if (waterRequiresFloor && !hasAquiferCupSupport(x, y, z, localThreshold)) {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
return carveWater;
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
if (!profile.isAllowLava() && y <= lavaHeight) {
|
||||
@@ -377,38 +359,6 @@ public class IrisCaveCarver3D {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
private boolean hasAquiferCupSupport(int x, int y, int z, double threshold) {
|
||||
int floorY = Math.max(0, y - 1);
|
||||
int deepFloorY = Math.max(0, y - 2);
|
||||
if (!isDensitySolid(x, floorY, z, threshold)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDensitySolid(x, deepFloorY, z, threshold - 0.05D)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int support = 0;
|
||||
if (isDensitySolid(x + 1, y, z, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x - 1, y, z, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x, y, z + 1, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x, y, z - 1, threshold)) {
|
||||
support++;
|
||||
}
|
||||
|
||||
return support >= 3;
|
||||
}
|
||||
|
||||
private boolean isDensitySolid(int x, int y, int z, double threshold) {
|
||||
return sampleDensity(x, y, z) > threshold;
|
||||
}
|
||||
|
||||
private double clampColumnWeight(double weight) {
|
||||
if (Double.isNaN(weight) || Double.isInfinite(weight)) {
|
||||
return 0D;
|
||||
|
||||
@@ -48,10 +48,15 @@ import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@ComponentFlag(ReservedFlag.OBJECT)
|
||||
public class MantleObjectComponent extends IrisMantleComponent {
|
||||
private static final long CAVE_REJECT_LOG_THROTTLE_MS = 5000L;
|
||||
private static final Map<String, CaveRejectLogState> CAVE_REJECT_LOG_STATE = new ConcurrentHashMap<>();
|
||||
|
||||
public MantleObjectComponent(EngineMantle engineMantle) {
|
||||
super(engineMantle, ReservedFlag.OBJECT, 1);
|
||||
@@ -66,11 +71,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
||||
IrisBiome surfaceBiome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||
int surfaceY = getEngineMantle().getEngine().getHeight(xxx, zzz, true);
|
||||
int sampleY = Math.max(1, surfaceY - 48);
|
||||
IrisBiome caveBiome = getEngineMantle().getEngine().getCaveBiome(xxx, sampleY, zzz);
|
||||
if (caveBiome == null) {
|
||||
caveBiome = surfaceBiome;
|
||||
}
|
||||
IrisBiome caveBiome = resolveCaveObjectBiome(xxx, zzz, surfaceY, surfaceBiome);
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object layer start: chunk=" + x + "," + z
|
||||
+ " surfaceBiome=" + surfaceBiome.getLoadKey()
|
||||
@@ -105,6 +106,41 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
return new RNG((long) (seed * noise.noise(x, z)));
|
||||
}
|
||||
|
||||
private IrisBiome resolveCaveObjectBiome(int x, int z, int surfaceY, IrisBiome surfaceBiome) {
|
||||
int legacySampleY = Math.max(1, surfaceY - 48);
|
||||
IrisBiome legacyCaveBiome = getEngineMantle().getEngine().getCaveBiome(x, legacySampleY, z);
|
||||
if (legacyCaveBiome == null) {
|
||||
legacyCaveBiome = surfaceBiome;
|
||||
}
|
||||
|
||||
int[] sampleDepths = new int[]{48, 80, 112};
|
||||
IrisBiome ladderChoice = null;
|
||||
for (int sampleDepth : sampleDepths) {
|
||||
int sampleY = Math.max(1, surfaceY - sampleDepth);
|
||||
IrisBiome sampled = getEngineMantle().getEngine().getCaveBiome(x, sampleY, z);
|
||||
boolean sameSurface = sampled == surfaceBiome;
|
||||
if (!sameSurface && sampled != null && surfaceBiome != null) {
|
||||
String sampledKey = sampled.getLoadKey();
|
||||
String surfaceKey = surfaceBiome.getLoadKey();
|
||||
sameSurface = sampledKey != null && sampledKey.equals(surfaceKey);
|
||||
}
|
||||
|
||||
if (sampled == null || sameSurface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sampled.getCarvingObjects().isEmpty()) {
|
||||
ladderChoice = sampled;
|
||||
}
|
||||
}
|
||||
|
||||
if (ladderChoice != null) {
|
||||
return ladderChoice;
|
||||
}
|
||||
|
||||
return legacyCaveBiome;
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome surfaceBiome, IrisBiome caveBiome, IrisRegion region, boolean traceRegen) {
|
||||
int biomeSurfaceChecked = 0;
|
||||
@@ -393,6 +429,22 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
+ " density=" + density
|
||||
+ " placementKeys=" + objectPlacement.getPlace().toString(","));
|
||||
}
|
||||
logCaveReject(
|
||||
scope,
|
||||
"NULL_OBJECT",
|
||||
metricChunkX,
|
||||
metricChunkZ,
|
||||
objectPlacement,
|
||||
null,
|
||||
i,
|
||||
density,
|
||||
anchorMode,
|
||||
anchorSearchAttempts,
|
||||
anchorScanStep,
|
||||
objectMinDepthBelowSurface,
|
||||
null,
|
||||
null
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -415,24 +467,59 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
|
||||
if (y < 0) {
|
||||
rejected++;
|
||||
logCaveReject(
|
||||
scope,
|
||||
"NO_ANCHOR",
|
||||
metricChunkX,
|
||||
metricChunkZ,
|
||||
objectPlacement,
|
||||
object,
|
||||
i,
|
||||
density,
|
||||
anchorMode,
|
||||
anchorSearchAttempts,
|
||||
anchorScanStep,
|
||||
objectMinDepthBelowSurface,
|
||||
null,
|
||||
null
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
int id = rng.i(0, Integer.MAX_VALUE);
|
||||
IrisObjectPlacement effectivePlacement = resolveEffectivePlacement(objectPlacement, object);
|
||||
AtomicBoolean wrotePlacementData = new AtomicBoolean(false);
|
||||
|
||||
try {
|
||||
int result = object.place(x, y, z, writer, effectivePlacement, rng, (b, data) -> {
|
||||
wrotePlacementData.set(true);
|
||||
writer.setData(b.getX(), b.getY(), b.getZ(), object.getLoadKey() + "@" + id);
|
||||
if (effectivePlacement.isDolphinTarget() && effectivePlacement.isUnderwater() && B.isStorageChest(data)) {
|
||||
writer.setData(b.getX(), b.getY(), b.getZ(), MatterStructurePOI.BURIED_TREASURE);
|
||||
}
|
||||
}, null, getData());
|
||||
|
||||
if (result >= 0) {
|
||||
boolean wroteBlocks = wrotePlacementData.get();
|
||||
if (wroteBlocks) {
|
||||
placed++;
|
||||
} else {
|
||||
} else if (result < 0) {
|
||||
rejected++;
|
||||
logCaveReject(
|
||||
scope,
|
||||
"PLACE_NEGATIVE",
|
||||
metricChunkX,
|
||||
metricChunkZ,
|
||||
objectPlacement,
|
||||
object,
|
||||
i,
|
||||
density,
|
||||
anchorMode,
|
||||
anchorSearchAttempts,
|
||||
anchorScanStep,
|
||||
objectMinDepthBelowSurface,
|
||||
y,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (traceRegen) {
|
||||
@@ -443,6 +530,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
+ " anchorY=" + y
|
||||
+ " px=" + x
|
||||
+ " pz=" + z
|
||||
+ " wroteBlocks=" + wroteBlocks
|
||||
+ " densityIndex=" + i
|
||||
+ " density=" + density);
|
||||
}
|
||||
@@ -455,13 +543,87 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
+ " densityIndex=" + i
|
||||
+ " density=" + density
|
||||
+ " error=" + e.getClass().getSimpleName() + ":" + e.getMessage());
|
||||
logCaveReject(
|
||||
scope,
|
||||
"EXCEPTION",
|
||||
metricChunkX,
|
||||
metricChunkZ,
|
||||
objectPlacement,
|
||||
object,
|
||||
i,
|
||||
density,
|
||||
anchorMode,
|
||||
anchorSearchAttempts,
|
||||
anchorScanStep,
|
||||
objectMinDepthBelowSurface,
|
||||
y,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new ObjectPlacementResult(attempts, placed, rejected, nullObjects, errors);
|
||||
}
|
||||
|
||||
private static IrisObjectPlacement resolveEffectivePlacement(IrisObjectPlacement objectPlacement, IrisObject object) {
|
||||
private void logCaveReject(
|
||||
String scope,
|
||||
String reason,
|
||||
int chunkX,
|
||||
int chunkZ,
|
||||
IrisObjectPlacement objectPlacement,
|
||||
IrisObject object,
|
||||
int densityIndex,
|
||||
int density,
|
||||
IrisCaveAnchorMode anchorMode,
|
||||
int anchorSearchAttempts,
|
||||
int anchorScanStep,
|
||||
int objectMinDepthBelowSurface,
|
||||
Integer anchorY,
|
||||
Throwable error
|
||||
) {
|
||||
if (!IrisSettings.get().getGeneral().isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String placementKeys = objectPlacement == null ? "none" : objectPlacement.getPlace().toString(",");
|
||||
String objectKey = object == null ? "null" : object.getLoadKey();
|
||||
String throttleKey = scope + "|" + reason + "|" + placementKeys + "|" + objectKey;
|
||||
CaveRejectLogState state = CAVE_REJECT_LOG_STATE.computeIfAbsent(throttleKey, (k) -> new CaveRejectLogState());
|
||||
long now = System.currentTimeMillis();
|
||||
long last = state.lastLogMs.get();
|
||||
if ((now - last) < CAVE_REJECT_LOG_THROTTLE_MS) {
|
||||
state.suppressed.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.lastLogMs.compareAndSet(last, now)) {
|
||||
state.suppressed.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
int suppressed = state.suppressed.getAndSet(0);
|
||||
String anchorYText = anchorY == null ? "none" : String.valueOf(anchorY);
|
||||
String errorText = error == null ? "none" : error.getClass().getSimpleName() + ":" + String.valueOf(error.getMessage());
|
||||
Iris.warn("Cave object reject: scope=" + scope
|
||||
+ " reason=" + reason
|
||||
+ " chunk=" + chunkX + "," + chunkZ
|
||||
+ " object=" + objectKey
|
||||
+ " placements=" + placementKeys
|
||||
+ " densityIndex=" + densityIndex
|
||||
+ " density=" + density
|
||||
+ " anchorMode=" + anchorMode
|
||||
+ " anchorSearchAttempts=" + anchorSearchAttempts
|
||||
+ " anchorScanStep=" + anchorScanStep
|
||||
+ " minDepthBelowSurface=" + objectMinDepthBelowSurface
|
||||
+ " anchorY=" + anchorYText
|
||||
+ " forcePlace=" + (objectPlacement != null && objectPlacement.isForcePlace())
|
||||
+ " carvingSupport=" + (objectPlacement == null ? "none" : objectPlacement.getCarvingSupport())
|
||||
+ " bottom=" + (objectPlacement != null && objectPlacement.isBottom())
|
||||
+ " suppressed=" + suppressed
|
||||
+ " error=" + errorText);
|
||||
}
|
||||
|
||||
private IrisObjectPlacement resolveEffectivePlacement(IrisObjectPlacement objectPlacement, IrisObject object) {
|
||||
if (objectPlacement == null || object == null) {
|
||||
return objectPlacement;
|
||||
}
|
||||
@@ -472,19 +634,20 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
|
||||
String normalized = loadKey.toLowerCase(Locale.ROOT);
|
||||
boolean imported = normalized.startsWith("imports/")
|
||||
boolean legacyImported = normalized.startsWith("imports/")
|
||||
|| normalized.contains("/imports/")
|
||||
|| normalized.contains("imports/");
|
||||
IrisExternalDatapack externalDatapack = resolveExternalDatapackForObjectKey(normalized);
|
||||
boolean externalImported = externalDatapack != null;
|
||||
boolean imported = legacyImported || externalImported;
|
||||
|
||||
if (!imported) {
|
||||
return objectPlacement;
|
||||
}
|
||||
|
||||
ObjectPlaceMode mode = objectPlacement.getMode();
|
||||
if (mode == ObjectPlaceMode.STILT
|
||||
|| mode == ObjectPlaceMode.FAST_STILT
|
||||
|| mode == ObjectPlaceMode.MIN_STILT
|
||||
|| mode == ObjectPlaceMode.FAST_MIN_STILT
|
||||
|| mode == ObjectPlaceMode.CENTER_STILT) {
|
||||
boolean needsModeChange = mode != ObjectPlaceMode.FAST_MIN_STILT;
|
||||
if (!needsModeChange) {
|
||||
return objectPlacement;
|
||||
}
|
||||
|
||||
@@ -493,6 +656,42 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
return effectivePlacement;
|
||||
}
|
||||
|
||||
private IrisExternalDatapack resolveExternalDatapackForObjectKey(String normalizedLoadKey) {
|
||||
if (normalizedLoadKey == null || normalizedLoadKey.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int slash = normalizedLoadKey.indexOf('/');
|
||||
if (slash <= 0) {
|
||||
return null;
|
||||
}
|
||||
String candidateId = normalizedLoadKey.substring(0, slash);
|
||||
if (candidateId.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IrisDimension dimension = getDimension();
|
||||
if (dimension == null || dimension.getExternalDatapacks() == null || dimension.getExternalDatapacks().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (IrisExternalDatapack externalDatapack : dimension.getExternalDatapacks()) {
|
||||
if (externalDatapack == null || !externalDatapack.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String id = externalDatapack.getId();
|
||||
if (id == null || id.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if (candidateId.equals(id.toLowerCase(Locale.ROOT))) {
|
||||
return externalDatapack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int findCaveAnchorY(MantleWriter writer, RNG rng, int x, int z, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, KMap<Long, KList<Integer>> anchorCache) {
|
||||
long key = Cache.key(x, z);
|
||||
KList<Integer> anchors = anchorCache.computeIfAbsent(key, (k) -> scanCaveAnchorColumn(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z));
|
||||
@@ -662,6 +861,11 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
private record ObjectPlacementResult(int attempts, int placed, int rejected, int nullObjects, int errors) {
|
||||
}
|
||||
|
||||
private static final class CaveRejectLogState {
|
||||
private final AtomicLong lastLogMs = new AtomicLong(0L);
|
||||
private final AtomicInteger suppressed = new AtomicInteger(0);
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
private Set<String> guessPlacedKeys(RNG rng, int x, int z, IrisObjectPlacement objectPlacement) {
|
||||
Set<String> f = new KSet<>();
|
||||
|
||||
@@ -242,8 +242,14 @@ public class IrisEntity extends IrisRegistrant {
|
||||
int gg = 0;
|
||||
for (IrisEntity i : passengers) {
|
||||
Entity passenger = i.spawn(gen, at, rng.nextParallelRNG(234858 + gg++));
|
||||
if (!Bukkit.isPrimaryThread()) {
|
||||
J.s(() -> e.addPassenger(passenger));
|
||||
if (passenger == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
e.addPassenger(passenger);
|
||||
} else {
|
||||
J.runEntity(e, () -> e.addPassenger(passenger));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +344,7 @@ public class IrisEntity extends IrisRegistrant {
|
||||
if (e instanceof Villager) {
|
||||
Villager villager = (Villager) e;
|
||||
villager.setRemoveWhenFarAway(false);
|
||||
J.s(() -> villager.setPersistent(true), 1);
|
||||
J.runEntity(villager, () -> villager.setPersistent(true), 1);
|
||||
}
|
||||
|
||||
if (e instanceof Mob) {
|
||||
@@ -365,7 +371,7 @@ public class IrisEntity extends IrisRegistrant {
|
||||
|
||||
Location finalAt1 = at;
|
||||
|
||||
J.s(() -> {
|
||||
J.runEntity(e, () -> {
|
||||
if (isSpawnEffectRiseOutOfGround() && e instanceof LivingEntity && Chunks.hasPlayersNearby(finalAt1)) {
|
||||
Location start = finalAt1.clone();
|
||||
e.setInvulnerable(true);
|
||||
@@ -373,10 +379,13 @@ public class IrisEntity extends IrisRegistrant {
|
||||
((LivingEntity) e).setCollidable(false);
|
||||
((LivingEntity) e).setNoDamageTicks(100000);
|
||||
AtomicInteger t = new AtomicInteger(0);
|
||||
AtomicInteger v = new AtomicInteger(0);
|
||||
v.set(J.sr(() -> {
|
||||
if (t.get() > 100) {
|
||||
J.csr(v.get());
|
||||
Runnable[] loop = new Runnable[1];
|
||||
loop[0] = () -> {
|
||||
if (t.get() > 100 || e.isDead()) {
|
||||
((LivingEntity) e).setNoDamageTicks(0);
|
||||
((LivingEntity) e).setCollidable(true);
|
||||
((LivingEntity) e).setAI(true);
|
||||
e.setInvulnerable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -388,14 +397,20 @@ public class IrisEntity extends IrisRegistrant {
|
||||
if (M.r(0.2)) {
|
||||
e.getWorld().playSound(e.getLocation(), Sound.BLOCK_CHORUS_FLOWER_GROW, 0.8f, 0.1f);
|
||||
}
|
||||
if (!J.runEntity(e, loop[0], 1)) {
|
||||
((LivingEntity) e).setNoDamageTicks(0);
|
||||
((LivingEntity) e).setCollidable(true);
|
||||
((LivingEntity) e).setAI(true);
|
||||
e.setInvulnerable(false);
|
||||
}
|
||||
} else {
|
||||
J.csr(v.get());
|
||||
((LivingEntity) e).setNoDamageTicks(0);
|
||||
((LivingEntity) e).setCollidable(true);
|
||||
((LivingEntity) e).setAI(true);
|
||||
e.setInvulnerable(false);
|
||||
}
|
||||
}, 0));
|
||||
};
|
||||
J.runEntity(e, loop[0]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -437,7 +452,9 @@ public class IrisEntity extends IrisRegistrant {
|
||||
AtomicReference<Entity> ae = new AtomicReference<>();
|
||||
|
||||
try {
|
||||
J.s(() -> ae.set(doSpawn(at)));
|
||||
if (!J.runAt(at, () -> ae.set(doSpawn(at)))) {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ public class IrisExternalDatapack {
|
||||
@Desc("If true, minecraft namespace worldgen assets may replace vanilla targets listed in replaceTargets")
|
||||
private boolean replaceVanilla = false;
|
||||
|
||||
@Desc("If true, structures projected from this datapack id receive smartbore foundation extension during generation")
|
||||
private boolean supportSmartBore = true;
|
||||
|
||||
@Desc("Explicit replacement targets for minecraft namespace assets")
|
||||
private IrisExternalDatapackReplaceTargets replaceTargets = new IrisExternalDatapackReplaceTargets();
|
||||
|
||||
|
||||
@@ -954,7 +954,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone();
|
||||
i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone();
|
||||
|
||||
if (stilting && i.getBlockY() < lowest && !B.isAir(data)) {
|
||||
if (stilting && i.getBlockY() < lowest && B.isSolid(data)) {
|
||||
lowest = i.getBlockY();
|
||||
}
|
||||
|
||||
@@ -1013,8 +1013,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((config.isWaterloggable() || config.isUnderwater()) && yy <= placer.getFluidHeight() && data instanceof Waterlogged) {
|
||||
// TODO Here
|
||||
if (data instanceof Waterlogged && shouldAutoWaterlogBlock(placer, config, yv, xx, yy, zz)) {
|
||||
((Waterlogged) data).setWaterlogged(true);
|
||||
}
|
||||
|
||||
@@ -1056,24 +1055,28 @@ public class IrisObject extends IrisRegistrant {
|
||||
readLock.lock();
|
||||
IrisStiltSettings settings = config.getStiltSettings();
|
||||
for (BlockVector g : blocks.keys()) {
|
||||
BlockData d;
|
||||
BlockData sourceData;
|
||||
try {
|
||||
sourceData = blocks.get(g);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)");
|
||||
sourceData = AIR;
|
||||
}
|
||||
|
||||
if (settings == null || settings.getPalette() == null) {
|
||||
try {
|
||||
d = blocks.get(g);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)");
|
||||
d = AIR;
|
||||
}
|
||||
if (sourceData == null) {
|
||||
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt null)");
|
||||
sourceData = AIR;
|
||||
}
|
||||
|
||||
if (d == null) {
|
||||
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt null)");
|
||||
d = AIR;
|
||||
}
|
||||
} else
|
||||
if (!B.isSolid(sourceData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockData d = sourceData;
|
||||
if (settings != null && settings.getPalette() != null) {
|
||||
d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata);
|
||||
|
||||
}
|
||||
|
||||
BlockVector i = g.clone();
|
||||
i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone();
|
||||
@@ -1099,7 +1102,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
}
|
||||
}
|
||||
|
||||
if (d == null || B.isAir(d))
|
||||
if (d == null || !B.isSolid(d))
|
||||
continue;
|
||||
|
||||
xx = x + (int) Math.round(i.getX());
|
||||
@@ -1112,7 +1115,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
|
||||
int highest = placer.getHighest(xx, zz, getLoader(), true);
|
||||
|
||||
if ((config.isWaterloggable() || config.isUnderwater()) && highest <= placer.getFluidHeight() && d instanceof Waterlogged)
|
||||
if (d instanceof Waterlogged && shouldAutoWaterlogBlock(placer, config, yv, xx, highest, zz))
|
||||
((Waterlogged) d).setWaterlogged(true);
|
||||
|
||||
if (yv >= 0 && config.isBottom())
|
||||
@@ -1177,6 +1180,23 @@ public class IrisObject extends IrisRegistrant {
|
||||
|| placer.isCarved(x, y - 3, z);
|
||||
}
|
||||
|
||||
private boolean shouldAutoWaterlogBlock(IObjectPlacer placer, IrisObjectPlacement placement, int yv, int x, int y, int z) {
|
||||
if (!(placement.isWaterloggable() || placement.isUnderwater())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (yv >= 0 && placement.getCarvingSupport().equals(CarvingMode.CARVING_ONLY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockData existing = placer.get(x, y, z);
|
||||
if (existing == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return B.isWater(existing) || B.isWaterLogged(existing);
|
||||
}
|
||||
|
||||
public IrisObject rotateCopy(IrisObjectRotation rt) {
|
||||
IrisObject copy = copy();
|
||||
copy.rotate(rt, 0, 0, 0);
|
||||
|
||||
@@ -201,7 +201,8 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
List<StructureStart> starts = new ArrayList<>(structureManager.startsForStructure(chunkAccess.getPos(), structure -> true));
|
||||
starts.sort(Comparator.comparingInt(start -> structureOrder.getOrDefault(start.getStructure(), Integer.MAX_VALUE)));
|
||||
Set<String> externalLocateStructures = ExternalDataPackPipeline.snapshotLocateStructureKeys();
|
||||
Set<String> externalSmartBoreStructures = ExternalDataPackPipeline.snapshotSmartBoreStructureKeys();
|
||||
Set<String> suppressedVanillaStructures = ExternalDataPackPipeline.snapshotSuppressedVanillaStructureKeys();
|
||||
|
||||
int seededStructureIndex = Integer.MIN_VALUE;
|
||||
for (int j = 0; j < starts.size(); j++) {
|
||||
@@ -213,17 +214,20 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
seededStructureIndex = structureIndex;
|
||||
}
|
||||
Supplier<String> supplier = () -> structureRegistry.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||
String structureKey = supplier.get().toLowerCase(Locale.ROOT);
|
||||
boolean isExternalLocateStructure = externalLocateStructures.contains(structureKey);
|
||||
String structureKey = resolveStructureKey(structureRegistry, structure);
|
||||
if (suppressedVanillaStructures.contains(structureKey)) {
|
||||
continue;
|
||||
}
|
||||
boolean isExternalSmartBoreStructure = externalSmartBoreStructures.contains(structureKey);
|
||||
BitSet[] beforeSolidColumns = null;
|
||||
if (isExternalLocateStructure) {
|
||||
if (isExternalSmartBoreStructure) {
|
||||
beforeSolidColumns = snapshotChunkSolidColumns(level, chunkAccess);
|
||||
}
|
||||
|
||||
try {
|
||||
level.setCurrentlyGenerating(supplier);
|
||||
start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos());
|
||||
if (isExternalLocateStructure && beforeSolidColumns != null) {
|
||||
if (isExternalSmartBoreStructure && beforeSolidColumns != null) {
|
||||
applyExternalStructureFoundations(level, chunkAccess, beforeSolidColumns, EXTERNAL_FOUNDATION_MAX_DEPTH);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
@@ -237,6 +241,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||
}
|
||||
|
||||
private static String resolveStructureKey(Registry<Structure> structureRegistry, Structure structure) {
|
||||
Identifier directKey = structureRegistry.getKey(structure);
|
||||
if (directKey != null) {
|
||||
return directKey.toString().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
String fallback = String.valueOf(structure);
|
||||
int slash = fallback.lastIndexOf('/');
|
||||
int end = fallback.lastIndexOf(']');
|
||||
if (slash >= 0 && end > slash) {
|
||||
return fallback.substring(slash + 1, end).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
return fallback.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||
int minX = chunkPos.getMinBlockX();
|
||||
|
||||
Reference in New Issue
Block a user