mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-03 06:16:19 +00:00
Working
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@ libs/
|
||||
collection/
|
||||
|
||||
/core/src/main/java/art/arcane/iris/util/uniques/
|
||||
|
||||
DataPackExamples/
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -257,7 +257,6 @@ public class IrisSettings {
|
||||
public boolean preventLeafDecay = true;
|
||||
public boolean useMulticore = false;
|
||||
public boolean useMulticoreMantle = false;
|
||||
public boolean offsetNoiseTypes = false;
|
||||
public boolean earlyCustomBlocks = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package art.arcane.iris.core;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
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;
|
||||
@@ -142,27 +143,156 @@ public class ServerConfigurator {
|
||||
return;
|
||||
}
|
||||
|
||||
File source = Iris.instance.getDataFolder("datapacks");
|
||||
source.mkdirs();
|
||||
ExternalDataPackPipeline.syncPinnedDatapacks(source);
|
||||
int removedLegacyCopies = ExternalDataPackPipeline.removeLegacyWorldDatapackCopies(source, folders);
|
||||
ExternalDataPackPipeline.ImportSummary summary = ExternalDataPackPipeline.importDatapackStructures(source);
|
||||
if (removedLegacyCopies > 0) {
|
||||
Iris.info("Removed " + removedLegacyCopies + " legacy external datapack world copies.");
|
||||
KList<ExternalDataPackPipeline.DatapackRequest> requests = collectExternalDatapackRequests();
|
||||
KMap<String, KList<File>> worldDatapackFoldersByPack = collectWorldDatapackFoldersByPack(folders);
|
||||
ExternalDataPackPipeline.PipelineSummary summary = ExternalDataPackPipeline.processDatapacks(requests, worldDatapackFoldersByPack);
|
||||
if (summary.getLegacyDownloadRemovals() > 0) {
|
||||
Iris.info("Removed " + summary.getLegacyDownloadRemovals() + " legacy global datapack downloads.");
|
||||
}
|
||||
if (summary.getSources() > 0) {
|
||||
Iris.info("External datapack structure import: sources=" + summary.getSources()
|
||||
+ ", cached=" + summary.getCachedSources()
|
||||
+ ", scanned=" + summary.getNbtScanned()
|
||||
+ ", converted=" + summary.getConverted()
|
||||
+ ", failed=" + summary.getFailed()
|
||||
+ ", skipped=" + summary.getSkipped()
|
||||
+ ", entitiesIgnored=" + summary.getEntitiesIgnored()
|
||||
+ ", blockEntities=" + summary.getBlockEntities());
|
||||
if (summary.getLegacyWorldCopyRemovals() > 0) {
|
||||
Iris.info("Removed " + summary.getLegacyWorldCopyRemovals() + " legacy managed world datapack copies.");
|
||||
}
|
||||
if (summary.getSources() > 0 || summary.getConverted() > 0) {
|
||||
Iris.info("External datapack world install is disabled; only structure template import is applied.");
|
||||
if (summary.getRequests() > 0 || summary.getImportedSources() > 0 || summary.getWorldDatapacksInstalled() > 0) {
|
||||
Iris.info("External datapack sync/import/install: requests=" + summary.getRequests()
|
||||
+ ", synced=" + summary.getSyncedRequests()
|
||||
+ ", restored=" + summary.getRestoredRequests()
|
||||
+ ", importedSources=" + summary.getImportedSources()
|
||||
+ ", cachedSources=" + summary.getCachedSources()
|
||||
+ ", converted=" + summary.getConvertedStructures()
|
||||
+ ", failedConversions=" + summary.getFailedConversions()
|
||||
+ ", worldDatapacks=" + summary.getWorldDatapacksInstalled()
|
||||
+ ", worldAssets=" + summary.getWorldAssetsInstalled()
|
||||
+ ", optionalFailures=" + summary.getOptionalFailures()
|
||||
+ ", requiredFailures=" + summary.getRequiredFailures());
|
||||
}
|
||||
if (summary.getRequiredFailures() > 0) {
|
||||
throw new IllegalStateException("Required external datapack setup failed for " + summary.getRequiredFailures() + " request(s).");
|
||||
}
|
||||
}
|
||||
|
||||
private static KList<ExternalDataPackPipeline.DatapackRequest> collectExternalDatapackRequests() {
|
||||
KMap<String, ExternalDataPackPipeline.DatapackRequest> deduplicated = new KMap<>();
|
||||
try (Stream<IrisData> stream = allPacks()) {
|
||||
stream.forEach(data -> {
|
||||
ResourceLoader<IrisDimension> loader = data.getDimensionLoader();
|
||||
if (loader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
KList<IrisDimension> dimensions = loader.loadAll(loader.getPossibleKeys());
|
||||
for (IrisDimension dimension : dimensions) {
|
||||
if (dimension == null || dimension.getExternalDatapacks() == null || dimension.getExternalDatapacks().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String targetPack = sanitizePackName(dimension.getLoadKey());
|
||||
if (targetPack.isBlank()) {
|
||||
targetPack = sanitizePackName(data.getDataFolder().getName());
|
||||
}
|
||||
String environment = ExternalDataPackPipeline.normalizeEnvironmentValue(dimension.getEnvironment() == null ? null : dimension.getEnvironment().name());
|
||||
|
||||
for (IrisExternalDatapack externalDatapack : dimension.getExternalDatapacks()) {
|
||||
if (externalDatapack == null || !externalDatapack.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String url = externalDatapack.getUrl() == null ? "" : externalDatapack.getUrl().trim();
|
||||
if (url.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String requestId = externalDatapack.getId() == null ? "" : externalDatapack.getId().trim();
|
||||
if (requestId.isBlank()) {
|
||||
requestId = url;
|
||||
}
|
||||
|
||||
IrisExternalDatapackReplaceTargets replaceTargets = externalDatapack.getReplaceTargets();
|
||||
ExternalDataPackPipeline.DatapackRequest request = new ExternalDataPackPipeline.DatapackRequest(
|
||||
requestId,
|
||||
url,
|
||||
targetPack,
|
||||
environment,
|
||||
externalDatapack.isRequired(),
|
||||
externalDatapack.isReplaceVanilla(),
|
||||
replaceTargets
|
||||
);
|
||||
|
||||
String dedupeKey = request.getDedupeKey();
|
||||
ExternalDataPackPipeline.DatapackRequest existing = deduplicated.get(dedupeKey);
|
||||
if (existing == null) {
|
||||
deduplicated.put(dedupeKey, request);
|
||||
continue;
|
||||
}
|
||||
|
||||
deduplicated.put(dedupeKey, existing.merge(request));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new KList<>(deduplicated.v());
|
||||
}
|
||||
|
||||
private static KMap<String, KList<File>> collectWorldDatapackFoldersByPack(KList<File> fallbackFolders) {
|
||||
KMap<String, KList<File>> foldersByPack = new KMap<>();
|
||||
KMap<String, String> mappedWorlds = IrisWorlds.get().getWorlds();
|
||||
|
||||
for (String worldName : mappedWorlds.k()) {
|
||||
String packName = sanitizePackName(mappedWorlds.get(worldName));
|
||||
if (packName.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
File datapacksFolder = new File(Bukkit.getWorldContainer(), worldName + File.separator + "datapacks");
|
||||
addWorldDatapackFolder(foldersByPack, packName, datapacksFolder);
|
||||
}
|
||||
|
||||
for (org.bukkit.World world : Bukkit.getWorlds()) {
|
||||
String worldName = world.getName();
|
||||
String mappedPack = mappedWorlds.get(worldName);
|
||||
String packName = sanitizePackName(mappedPack);
|
||||
if (packName.isBlank()) {
|
||||
packName = sanitizePackName(IrisSettings.get().getGenerator().getDefaultWorldType());
|
||||
}
|
||||
if (packName.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
File datapacksFolder = new File(world.getWorldFolder(), "datapacks");
|
||||
addWorldDatapackFolder(foldersByPack, packName, datapacksFolder);
|
||||
}
|
||||
|
||||
String defaultPack = sanitizePackName(IrisSettings.get().getGenerator().getDefaultWorldType());
|
||||
if (!defaultPack.isBlank()) {
|
||||
for (File folder : fallbackFolders) {
|
||||
addWorldDatapackFolder(foldersByPack, defaultPack, folder);
|
||||
}
|
||||
}
|
||||
|
||||
return foldersByPack;
|
||||
}
|
||||
|
||||
private static void addWorldDatapackFolder(KMap<String, KList<File>> foldersByPack, String packName, File folder) {
|
||||
if (folder == null || packName == null || packName.isBlank()) {
|
||||
return;
|
||||
}
|
||||
KList<File> folders = foldersByPack.computeIfAbsent(packName, k -> new KList<>());
|
||||
if (!folders.contains(folder)) {
|
||||
folders.add(folder);
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitizePackName(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String sanitized = value.trim().toLowerCase().replace("\\", "/");
|
||||
sanitized = sanitized.replaceAll("[^a-z0-9_\\-./]", "_");
|
||||
sanitized = sanitized.replaceAll("/+", "/");
|
||||
sanitized = sanitized.replaceAll("^/+", "");
|
||||
sanitized = sanitized.replaceAll("/+$", "");
|
||||
if (sanitized.contains("..")) {
|
||||
sanitized = sanitized.replace("..", "_");
|
||||
}
|
||||
return sanitized.replace("/", "_");
|
||||
}
|
||||
|
||||
private static boolean verifyDataPacksPost(boolean allowRestarting) {
|
||||
|
||||
@@ -114,22 +114,4 @@ public class CommandEdit implements DirectorExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
@Director(description = "Edit the cave file you specified", aliases = {"c"}, origin = DirectorOrigin.PLAYER)
|
||||
public void cave(@Param(contextual = false, description = "The cave to edit") IrisCave cave) {
|
||||
if (noStudio()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (cave == null || cave.getLoadFile() == null) {
|
||||
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
|
||||
return;
|
||||
}
|
||||
Desktop.getDesktop().open(cave.getLoadFile());
|
||||
sender().sendMessage(C.GREEN + "Opening " + cave.getTypeName() + " " + cave.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,8 +74,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
private ResourceLoader<IrisMatterObject> matterLoader;
|
||||
private ResourceLoader<IrisImage> imageLoader;
|
||||
private ResourceLoader<IrisScript> scriptLoader;
|
||||
private ResourceLoader<IrisCave> caveLoader;
|
||||
private ResourceLoader<IrisRavine> ravineLoader;
|
||||
private ResourceLoader<IrisMatterObject> matterObjectLoader;
|
||||
private KMap<String, KList<String>> possibleSnippets;
|
||||
private Gson gson;
|
||||
@@ -158,10 +156,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
return loadAny(IrisScript.class, key, nearest);
|
||||
}
|
||||
|
||||
public static IrisRavine loadAnyRavine(String key, @Nullable IrisData nearest) {
|
||||
return loadAny(IrisRavine.class, key, nearest);
|
||||
}
|
||||
|
||||
public static IrisRegion loadAnyRegion(String key, @Nullable IrisData nearest) {
|
||||
return loadAny(IrisRegion.class, key, nearest);
|
||||
}
|
||||
@@ -170,10 +164,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
return loadAny(IrisMarker.class, key, nearest);
|
||||
}
|
||||
|
||||
public static IrisCave loadAnyCave(String key, @Nullable IrisData nearest) {
|
||||
return loadAny(IrisCave.class, key, nearest);
|
||||
}
|
||||
|
||||
public static IrisImage loadAnyImage(String key, @Nullable IrisData nearest) {
|
||||
return loadAny(IrisImage.class, key, nearest);
|
||||
}
|
||||
@@ -352,9 +342,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
this.modLoader = registerLoader(IrisMod.class);
|
||||
this.dimensionLoader = registerLoader(IrisDimension.class);
|
||||
this.generatorLoader = registerLoader(IrisGenerator.class);
|
||||
this.caveLoader = registerLoader(IrisCave.class);
|
||||
this.markerLoader = registerLoader(IrisMarker.class);
|
||||
this.ravineLoader = registerLoader(IrisRavine.class);
|
||||
this.blockLoader = registerLoader(IrisBlockData.class);
|
||||
this.expressionLoader = registerLoader(IrisExpression.class);
|
||||
this.objectLoader = registerLoader(IrisObject.class);
|
||||
|
||||
@@ -119,7 +119,6 @@ public class IrisComplex implements DataProvider {
|
||||
.forEach(this::registerGenerators));
|
||||
}
|
||||
generatorBounds = buildGeneratorBounds(engine);
|
||||
boolean legacy = engine.getDimension().isLegacyRarity();
|
||||
KList<IrisShapedGeneratorStyle> overlayNoise = engine.getDimension().getOverlayNoise();
|
||||
overlayStream = overlayNoise.isEmpty()
|
||||
? ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream")
|
||||
@@ -143,7 +142,7 @@ public class IrisComplex implements DataProvider {
|
||||
ProceduralStream.of((x, z) -> focusRegion,
|
||||
Interpolated.of(a -> 0D, a -> focusRegion))
|
||||
: regionStyleStream
|
||||
.selectRarity(data.getRegionLoader().loadAll(engine.getDimension().getRegions()), legacy)
|
||||
.selectRarity(data.getRegionLoader().loadAll(engine.getDimension().getRegions()))
|
||||
.cache2D("regionStream", engine, cacheSize).waste("Region Stream");
|
||||
regionIDStream = regionIdentityStream.convertCached((i) -> new UUID(Double.doubleToLongBits(i),
|
||||
String.valueOf(i * 38445).hashCode() * 3245556666L)).waste("Region ID Stream");
|
||||
@@ -152,7 +151,7 @@ public class IrisComplex implements DataProvider {
|
||||
-> engine.getDimension().getCaveBiomeStyle().create(rng.nextParallelRNG(InferredType.CAVE.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(r.getCaveBiomeZoom())
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getCaveBiomes()), legacy)
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getCaveBiomes()))
|
||||
.onNull(emptyBiome)
|
||||
).convertAware2D(ProceduralStream::get).cache2D("caveBiomeStream", engine, cacheSize).waste("Cave Biome Stream");
|
||||
inferredStreams.put(InferredType.CAVE, caveBiomeStream);
|
||||
@@ -162,7 +161,7 @@ public class IrisComplex implements DataProvider {
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(engine.getDimension().getLandZoom())
|
||||
.zoom(r.getLandBiomeZoom())
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getLandBiomes(), (t) -> t.setInferredType(InferredType.LAND)), legacy)
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getLandBiomes(), (t) -> t.setInferredType(InferredType.LAND)))
|
||||
).convertAware2D(ProceduralStream::get)
|
||||
.cache2D("landBiomeStream", engine, cacheSize).waste("Land Biome Stream");
|
||||
inferredStreams.put(InferredType.LAND, landBiomeStream);
|
||||
@@ -172,7 +171,7 @@ public class IrisComplex implements DataProvider {
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(engine.getDimension().getSeaZoom())
|
||||
.zoom(r.getSeaBiomeZoom())
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getSeaBiomes(), (t) -> t.setInferredType(InferredType.SEA)), legacy)
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getSeaBiomes(), (t) -> t.setInferredType(InferredType.SEA)))
|
||||
).convertAware2D(ProceduralStream::get)
|
||||
.cache2D("seaBiomeStream", engine, cacheSize).waste("Sea Biome Stream");
|
||||
inferredStreams.put(InferredType.SEA, seaBiomeStream);
|
||||
@@ -181,7 +180,7 @@ public class IrisComplex implements DataProvider {
|
||||
-> engine.getDimension().getShoreBiomeStyle().create(rng.nextParallelRNG(InferredType.SHORE.ordinal()), getData()).stream()
|
||||
.zoom(engine.getDimension().getBiomeZoom())
|
||||
.zoom(r.getShoreBiomeZoom())
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getShoreBiomes(), (t) -> t.setInferredType(InferredType.SHORE)), legacy)
|
||||
.selectRarity(data.getBiomeLoader().loadAll(r.getShoreBiomes(), (t) -> t.setInferredType(InferredType.SHORE)))
|
||||
).convertAware2D(ProceduralStream::get).cache2D("shoreBiomeStream", engine, cacheSize).waste("Shore Biome Stream");
|
||||
inferredStreams.put(InferredType.SHORE, shoreBiomeStream);
|
||||
bridgeStream = focusBiome != null ? ProceduralStream.of((x, z) -> focusBiome.getInferredType(),
|
||||
|
||||
@@ -41,6 +41,7 @@ import art.arcane.volmlib.util.mantle.runtime.MantleDataAdapter;
|
||||
import art.arcane.volmlib.util.mantle.runtime.MantleHooks;
|
||||
import art.arcane.volmlib.util.mantle.runtime.TectonicPlate;
|
||||
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
||||
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
import art.arcane.volmlib.util.matter.IrisMatter;
|
||||
@@ -153,7 +154,14 @@ public class IrisEngineMantle implements EngineMantle {
|
||||
}
|
||||
|
||||
private Set<MantleFlag> getDisabledFlags() {
|
||||
return disabledFlags.aquire(() -> Set.copyOf(getDimension().getDisabledComponents()));
|
||||
return disabledFlags.aquire(() -> {
|
||||
KList<MantleFlag> disabled = new KList<>();
|
||||
disabled.addAll(getDimension().getDisabledComponents());
|
||||
if (!getDimension().isCarvingEnabled()) {
|
||||
disabled.addIfMissing(ReservedFlag.CARVED);
|
||||
}
|
||||
return Set.copyOf(disabled);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,6 +29,8 @@ import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IrisCaveCarver3D {
|
||||
private static final byte LIQUID_AIR = 0;
|
||||
private static final byte LIQUID_WATER = 1;
|
||||
@@ -81,6 +83,27 @@ public class IrisCaveCarver3D {
|
||||
}
|
||||
|
||||
public int carve(MantleWriter writer, int chunkX, int chunkZ) {
|
||||
double[] fullWeights = new double[256];
|
||||
Arrays.fill(fullWeights, 1D);
|
||||
return carve(writer, chunkX, chunkZ, fullWeights, 0D, 0D);
|
||||
}
|
||||
|
||||
public int carve(
|
||||
MantleWriter writer,
|
||||
int chunkX,
|
||||
int chunkZ,
|
||||
double[] columnWeights,
|
||||
double minWeight,
|
||||
double thresholdPenalty
|
||||
) {
|
||||
if (columnWeights == null || columnWeights.length < 256) {
|
||||
double[] fullWeights = new double[256];
|
||||
Arrays.fill(fullWeights, 1D);
|
||||
columnWeights = fullWeights;
|
||||
}
|
||||
|
||||
double resolvedMinWeight = Math.max(0D, Math.min(1D, minWeight));
|
||||
double resolvedThresholdPenalty = Math.max(0D, thresholdPenalty);
|
||||
int worldHeight = writer.getMantle().getWorldHeight();
|
||||
int minY = Math.max(0, (int) Math.floor(profile.getVerticalRange().getMin()));
|
||||
int maxY = Math.min(worldHeight - 1, (int) Math.ceil(profile.getVerticalRange().getMax()));
|
||||
@@ -140,6 +163,9 @@ public class IrisCaveCarver3D {
|
||||
surfaceBreakFloorY,
|
||||
surfaceBreakColumn,
|
||||
columnThreshold,
|
||||
columnWeights,
|
||||
resolvedMinWeight,
|
||||
resolvedThresholdPenalty,
|
||||
0D,
|
||||
false
|
||||
);
|
||||
@@ -162,6 +188,9 @@ public class IrisCaveCarver3D {
|
||||
surfaceBreakFloorY,
|
||||
surfaceBreakColumn,
|
||||
columnThreshold,
|
||||
columnWeights,
|
||||
resolvedMinWeight,
|
||||
resolvedThresholdPenalty,
|
||||
recoveryThresholdBoost,
|
||||
true
|
||||
);
|
||||
@@ -185,6 +214,9 @@ public class IrisCaveCarver3D {
|
||||
int[] surfaceBreakFloorY,
|
||||
boolean[] surfaceBreakColumn,
|
||||
double[] columnThreshold,
|
||||
double[] columnWeights,
|
||||
double minWeight,
|
||||
double thresholdPenalty,
|
||||
double thresholdBoost,
|
||||
boolean skipExistingCarved
|
||||
) {
|
||||
@@ -195,6 +227,11 @@ public class IrisCaveCarver3D {
|
||||
for (int lz = 0; lz < 16; lz++) {
|
||||
int z = z0 + lz;
|
||||
int index = (lx << 4) | lz;
|
||||
double columnWeight = clampColumnWeight(columnWeights[index]);
|
||||
if (columnWeight <= minWeight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int columnTopY = columnMaxY[index];
|
||||
if (columnTopY < minY) {
|
||||
continue;
|
||||
@@ -203,7 +240,7 @@ public class IrisCaveCarver3D {
|
||||
boolean breakColumn = surfaceBreakColumn[index];
|
||||
int breakFloorY = surfaceBreakFloorY[index];
|
||||
int surfaceY = columnSurface[index];
|
||||
double threshold = columnThreshold[index] + thresholdBoost;
|
||||
double threshold = columnThreshold[index] + thresholdBoost - ((1D - columnWeight) * thresholdPenalty);
|
||||
|
||||
for (int y = minY; y <= columnTopY; y += sampleStep) {
|
||||
double localThreshold = threshold;
|
||||
@@ -353,6 +390,22 @@ public class IrisCaveCarver3D {
|
||||
return sampleDensity(x, y, z) > threshold;
|
||||
}
|
||||
|
||||
private double clampColumnWeight(double weight) {
|
||||
if (Double.isNaN(weight) || Double.isInfinite(weight)) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
if (weight <= 0D) {
|
||||
return 0D;
|
||||
}
|
||||
|
||||
if (weight >= 1D) {
|
||||
return 1D;
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
private double signed(double value) {
|
||||
return (value * 2D) - 1D;
|
||||
}
|
||||
|
||||
@@ -18,26 +18,33 @@
|
||||
|
||||
package art.arcane.iris.engine.mantle.components;
|
||||
|
||||
import art.arcane.iris.engine.data.cache.Cache;
|
||||
import art.arcane.iris.engine.mantle.ComponentFlag;
|
||||
import art.arcane.iris.engine.mantle.EngineMantle;
|
||||
import art.arcane.iris.engine.mantle.IrisMantleComponent;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisCarving;
|
||||
import art.arcane.iris.engine.object.IrisCaveProfile;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisRegion;
|
||||
import art.arcane.iris.util.project.context.ChunkContext;
|
||||
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
||||
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ComponentFlag(ReservedFlag.CARVED)
|
||||
public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
private static final int CHUNK_SIZE = 16;
|
||||
private static final int CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE;
|
||||
private static final int BLEND_RADIUS = 3;
|
||||
private static final int FIELD_SIZE = CHUNK_SIZE + (BLEND_RADIUS * 2);
|
||||
private static final double MIN_WEIGHT = 0.08D;
|
||||
private static final double THRESHOLD_PENALTY = 0.24D;
|
||||
|
||||
private final Map<IrisCaveProfile, IrisCaveCarver3D> profileCarvers = new IdentityHashMap<>();
|
||||
|
||||
public MantleCarvingComponent(EngineMantle engineMantle) {
|
||||
@@ -46,71 +53,147 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
|
||||
@Override
|
||||
public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) {
|
||||
RNG rng = new RNG(Cache.key(x, z) + seed());
|
||||
int xxx = 8 + (x << 4);
|
||||
int zzz = 8 + (z << 4);
|
||||
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
||||
IrisBiome surfaceBiome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||
IrisCaveProfile caveBiomeProfile = resolveDominantCaveBiomeProfile(x, z);
|
||||
carve(writer, rng, x, z, region, surfaceBiome, caveBiomeProfile);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private void carve(MantleWriter writer, RNG rng, int cx, int cz, IrisRegion region, IrisBiome surfaceBiome, IrisCaveProfile caveBiomeProfile) {
|
||||
IrisCaveProfile dimensionProfile = getDimension().getCaveProfile();
|
||||
IrisCaveProfile surfaceBiomeProfile = surfaceBiome.getCaveProfile();
|
||||
IrisCaveProfile regionProfile = region.getCaveProfile();
|
||||
IrisCaveProfile activeProfile = resolveActiveProfile(dimensionProfile, regionProfile, surfaceBiomeProfile, caveBiomeProfile);
|
||||
if (isProfileEnabled(activeProfile)) {
|
||||
int carved = carveProfile(activeProfile, writer, cx, cz);
|
||||
if (carved > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeProfile != regionProfile && isProfileEnabled(regionProfile)) {
|
||||
carved = carveProfile(regionProfile, writer, cx, cz);
|
||||
if (carved > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeProfile != surfaceBiomeProfile && isProfileEnabled(surfaceBiomeProfile)) {
|
||||
carved = carveProfile(surfaceBiomeProfile, writer, cx, cz);
|
||||
if (carved > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeProfile != dimensionProfile && isProfileEnabled(dimensionProfile)) {
|
||||
carved = carveProfile(dimensionProfile, writer, cx, cz);
|
||||
if (carved > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
List<WeightedProfile> weightedProfiles = resolveWeightedProfiles(x, z);
|
||||
for (WeightedProfile weightedProfile : weightedProfiles) {
|
||||
carveProfile(weightedProfile.profile, weightedProfile.columnWeights, writer, x, z);
|
||||
}
|
||||
|
||||
carve(getDimension().getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||
carve(surfaceBiome.getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||
carve(region.getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private void carve(IrisCarving carving, MantleWriter writer, RNG rng, int cx, int cz) {
|
||||
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
|
||||
}
|
||||
|
||||
private RNG nextCarveRng(RNG rng, int cx, int cz) {
|
||||
return new RNG((rng.nextLong() * cx) + 490495L + cz);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private int carveProfile(IrisCaveProfile profile, MantleWriter writer, int cx, int cz) {
|
||||
if (!isProfileEnabled(profile)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void carveProfile(IrisCaveProfile profile, double[] columnWeights, MantleWriter writer, int cx, int cz) {
|
||||
IrisCaveCarver3D carver = getCarver(profile);
|
||||
return carver.carve(writer, cx, cz);
|
||||
carver.carve(writer, cx, cz, columnWeights, MIN_WEIGHT, THRESHOLD_PENALTY);
|
||||
}
|
||||
|
||||
private List<WeightedProfile> resolveWeightedProfiles(int chunkX, int chunkZ) {
|
||||
IrisCaveProfile[] profileField = buildProfileField(chunkX, chunkZ);
|
||||
Map<IrisCaveProfile, double[]> profileWeights = new IdentityHashMap<>();
|
||||
|
||||
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
|
||||
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
|
||||
int columnIndex = (localX << 4) | localZ;
|
||||
Map<IrisCaveProfile, Double> columnInfluence = sampleColumnInfluence(profileField, localX, localZ);
|
||||
for (Map.Entry<IrisCaveProfile, Double> entry : columnInfluence.entrySet()) {
|
||||
double[] weights = profileWeights.computeIfAbsent(entry.getKey(), key -> new double[CHUNK_AREA]);
|
||||
weights[columnIndex] = entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<WeightedProfile> weightedProfiles = new ArrayList<>();
|
||||
for (Map.Entry<IrisCaveProfile, double[]> entry : profileWeights.entrySet()) {
|
||||
IrisCaveProfile profile = entry.getKey();
|
||||
double[] weights = entry.getValue();
|
||||
double totalWeight = 0D;
|
||||
double maxWeight = 0D;
|
||||
|
||||
for (double weight : weights) {
|
||||
totalWeight += weight;
|
||||
if (weight > maxWeight) {
|
||||
maxWeight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxWeight < MIN_WEIGHT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double averageWeight = totalWeight / CHUNK_AREA;
|
||||
weightedProfiles.add(new WeightedProfile(profile, weights, averageWeight));
|
||||
}
|
||||
|
||||
weightedProfiles.sort(Comparator.comparingDouble(WeightedProfile::averageWeight));
|
||||
return weightedProfiles;
|
||||
}
|
||||
|
||||
private Map<IrisCaveProfile, Double> sampleColumnInfluence(IrisCaveProfile[] profileField, int localX, int localZ) {
|
||||
Map<IrisCaveProfile, Double> profileBlend = new IdentityHashMap<>();
|
||||
int centerX = localX + BLEND_RADIUS;
|
||||
int centerZ = localZ + BLEND_RADIUS;
|
||||
double totalKernelWeight = 0D;
|
||||
|
||||
for (int offsetX = -BLEND_RADIUS; offsetX <= BLEND_RADIUS; offsetX++) {
|
||||
for (int offsetZ = -BLEND_RADIUS; offsetZ <= BLEND_RADIUS; offsetZ++) {
|
||||
int sampleX = centerX + offsetX;
|
||||
int sampleZ = centerZ + offsetZ;
|
||||
IrisCaveProfile profile = profileField[(sampleX * FIELD_SIZE) + sampleZ];
|
||||
if (!isProfileEnabled(profile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double kernelWeight = haloWeight(offsetX, offsetZ);
|
||||
profileBlend.merge(profile, kernelWeight, Double::sum);
|
||||
totalKernelWeight += kernelWeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalKernelWeight <= 0D || profileBlend.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<IrisCaveProfile, Double> normalized = new IdentityHashMap<>();
|
||||
for (Map.Entry<IrisCaveProfile, Double> entry : profileBlend.entrySet()) {
|
||||
normalized.put(entry.getKey(), entry.getValue() / totalKernelWeight);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private IrisCaveProfile[] buildProfileField(int chunkX, int chunkZ) {
|
||||
IrisCaveProfile[] profileField = new IrisCaveProfile[FIELD_SIZE * FIELD_SIZE];
|
||||
int startX = (chunkX << 4) - BLEND_RADIUS;
|
||||
int startZ = (chunkZ << 4) - BLEND_RADIUS;
|
||||
|
||||
for (int fieldX = 0; fieldX < FIELD_SIZE; fieldX++) {
|
||||
int worldX = startX + fieldX;
|
||||
for (int fieldZ = 0; fieldZ < FIELD_SIZE; fieldZ++) {
|
||||
int worldZ = startZ + fieldZ;
|
||||
profileField[(fieldX * FIELD_SIZE) + fieldZ] = resolveColumnProfile(worldX, worldZ);
|
||||
}
|
||||
}
|
||||
|
||||
return profileField;
|
||||
}
|
||||
|
||||
private double haloWeight(int offsetX, int offsetZ) {
|
||||
int edgeDistance = Math.max(Math.abs(offsetX), Math.abs(offsetZ));
|
||||
return (BLEND_RADIUS + 1D) - edgeDistance;
|
||||
}
|
||||
|
||||
private IrisCaveProfile resolveColumnProfile(int worldX, int worldZ) {
|
||||
IrisCaveProfile resolved = null;
|
||||
IrisCaveProfile dimensionProfile = getDimension().getCaveProfile();
|
||||
if (isProfileEnabled(dimensionProfile)) {
|
||||
resolved = dimensionProfile;
|
||||
}
|
||||
|
||||
IrisRegion region = getComplex().getRegionStream().get(worldX, worldZ);
|
||||
if (region != null) {
|
||||
IrisCaveProfile regionProfile = region.getCaveProfile();
|
||||
if (isProfileEnabled(regionProfile)) {
|
||||
resolved = regionProfile;
|
||||
}
|
||||
}
|
||||
|
||||
IrisBiome surfaceBiome = getComplex().getTrueBiomeStream().get(worldX, worldZ);
|
||||
if (surfaceBiome != null) {
|
||||
IrisCaveProfile surfaceProfile = surfaceBiome.getCaveProfile();
|
||||
if (isProfileEnabled(surfaceProfile)) {
|
||||
resolved = surfaceProfile;
|
||||
}
|
||||
}
|
||||
|
||||
int surfaceY = getEngineMantle().getEngine().getHeight(worldX, worldZ, true);
|
||||
int sampleY = Math.max(1, surfaceY - 56);
|
||||
IrisBiome caveBiome = getEngineMantle().getEngine().getCaveBiome(worldX, sampleY, worldZ);
|
||||
if (caveBiome != null) {
|
||||
IrisCaveProfile caveProfile = caveBiome.getCaveProfile();
|
||||
if (isProfileEnabled(caveProfile)) {
|
||||
resolved = caveProfile;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private IrisCaveCarver3D getCarver(IrisCaveProfile profile) {
|
||||
@@ -130,82 +213,23 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
return profile != null && profile.isEnabled();
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private IrisCaveProfile resolveDominantCaveBiomeProfile(int chunkX, int chunkZ) {
|
||||
int[] offsets = new int[]{1, 4, 8, 12, 15};
|
||||
Map<IrisCaveProfile, Integer> profileVotes = new IdentityHashMap<>();
|
||||
int validSamples = 0;
|
||||
IrisCaveProfile dominantProfile = null;
|
||||
int dominantVotes = 0;
|
||||
|
||||
for (int offsetX : offsets) {
|
||||
for (int offsetZ : offsets) {
|
||||
int sampleX = (chunkX << 4) + offsetX;
|
||||
int sampleZ = (chunkZ << 4) + offsetZ;
|
||||
int surfaceY = getEngineMantle().getEngine().getHeight(sampleX, sampleZ, true);
|
||||
int sampleY = Math.max(1, surfaceY - 56);
|
||||
IrisBiome caveBiome = getEngineMantle().getEngine().getCaveBiome(sampleX, sampleY, sampleZ);
|
||||
if (caveBiome == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IrisCaveProfile profile = caveBiome.getCaveProfile();
|
||||
if (!isProfileEnabled(profile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int votes = profileVotes.getOrDefault(profile, 0) + 1;
|
||||
profileVotes.put(profile, votes);
|
||||
validSamples++;
|
||||
if (votes > dominantVotes) {
|
||||
dominantVotes = votes;
|
||||
dominantProfile = profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dominantProfile == null || validSamples <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int requiredVotes = Math.max(13, (int) Math.ceil(validSamples * 0.65D));
|
||||
if (dominantVotes < requiredVotes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dominantProfile;
|
||||
}
|
||||
|
||||
private IrisCaveProfile resolveActiveProfile(IrisCaveProfile dimensionProfile, IrisCaveProfile regionProfile, IrisCaveProfile surfaceBiomeProfile, IrisCaveProfile caveBiomeProfile) {
|
||||
if (isProfileEnabled(caveBiomeProfile)) {
|
||||
return caveBiomeProfile;
|
||||
}
|
||||
|
||||
if (isProfileEnabled(surfaceBiomeProfile)) {
|
||||
return surfaceBiomeProfile;
|
||||
}
|
||||
|
||||
if (isProfileEnabled(regionProfile)) {
|
||||
return regionProfile;
|
||||
}
|
||||
|
||||
return dimensionProfile;
|
||||
}
|
||||
|
||||
protected int computeRadius() {
|
||||
IrisDimension dimension = getDimension();
|
||||
int max = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
|
||||
private static final class WeightedProfile {
|
||||
private final IrisCaveProfile profile;
|
||||
private final double[] columnWeights;
|
||||
private final double averageWeight;
|
||||
|
||||
for (IrisRegion i : dimension.getAllRegions(this::getData)) {
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||
private WeightedProfile(IrisCaveProfile profile, double[] columnWeights, double averageWeight) {
|
||||
this.profile = profile;
|
||||
this.columnWeights = columnWeights;
|
||||
this.averageWeight = averageWeight;
|
||||
}
|
||||
|
||||
for (IrisBiome i : dimension.getAllBiomes(this::getData)) {
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||
private double averageWeight() {
|
||||
return averageWeight;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import art.arcane.iris.util.project.stream.interpolation.Interpolated;
|
||||
import java.util.List;
|
||||
|
||||
public interface IRare {
|
||||
static <T extends IRare> ProceduralStream<T> stream(ProceduralStream<Double> noise, List<T> possibilities, boolean legacyRarity) {
|
||||
return ProceduralStream.of(legacyRarity ? (x, z) -> pickLegacy(possibilities, noise.get(x, z)) : (x, z) -> pick(possibilities, noise.get(x, z)),
|
||||
legacyRarity ? (x, y, z) -> pickLegacy(possibilities, noise.get(x, y, z)) : (x, y, z) -> pick(possibilities, noise.get(x, y, z)),
|
||||
static <T extends IRare> ProceduralStream<T> stream(ProceduralStream<Double> noise, List<T> possibilities) {
|
||||
return ProceduralStream.of((x, z) -> pick(possibilities, noise.get(x, z)),
|
||||
(x, y, z) -> pick(possibilities, noise.get(x, y, z)),
|
||||
new Interpolated<T>() {
|
||||
@Override
|
||||
public double toDouble(T t) {
|
||||
@@ -89,63 +89,6 @@ public interface IRare {
|
||||
return possibilities.getLast();
|
||||
}
|
||||
|
||||
static <T extends IRare> T pickLegacy(List<T> possibilities, double noiseValue) {
|
||||
if (possibilities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (possibilities.size() == 1) {
|
||||
return possibilities.get(0);
|
||||
}
|
||||
int totalWeight = 0; // This is he baseline
|
||||
int buffer = 0;
|
||||
for (T i : possibilities) { // Im adding all of the rarity together
|
||||
totalWeight += i.getRarity();
|
||||
}
|
||||
double threshold = totalWeight * (possibilities.size() - 1) * noiseValue;
|
||||
for (T i : possibilities) {
|
||||
buffer += totalWeight - i.getRarity();
|
||||
|
||||
if (buffer >= threshold) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return possibilities.get(possibilities.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
static <T extends IRare> T pickOld(List<T> possibilities, double noiseValue) {
|
||||
if (possibilities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (possibilities.size() == 1) {
|
||||
return possibilities.get(0);
|
||||
}
|
||||
|
||||
double completeWeight = 0.0;
|
||||
double highestWeight = 0.0;
|
||||
|
||||
for (T item : possibilities) {
|
||||
double weight = Math.max(item.getRarity(), 1);
|
||||
highestWeight = Math.max(highestWeight, weight);
|
||||
completeWeight += weight;
|
||||
}
|
||||
|
||||
double r = noiseValue * completeWeight;
|
||||
double countWeight = 0.0;
|
||||
|
||||
for (T item : possibilities) {
|
||||
double weight = Math.max(highestWeight - Math.max(item.getRarity(), 1), 1);
|
||||
countWeight += weight;
|
||||
if (countWeight >= r) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return possibilities.get(possibilities.size() - 1);
|
||||
}
|
||||
|
||||
static int get(Object v) {
|
||||
return v instanceof IRare ? Math.max(1, ((IRare) v).getRarity()) : 1;
|
||||
}
|
||||
|
||||
@@ -100,8 +100,6 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
private boolean lockLayers = false;
|
||||
@Desc("The max layers to iterate below the surface for locked layer biomes (mesa).")
|
||||
private int lockLayersMax = 7;
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Profile-driven 3D cave configuration")
|
||||
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.ArrayType;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Snippet("carving")
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Represents a carving configuration")
|
||||
@Data
|
||||
public class IrisCarving {
|
||||
@ArrayType(type = IrisCavePlacer.class, min = 1)
|
||||
@Desc("Define cave placers")
|
||||
private KList<IrisCavePlacer> caves = new KList<>();
|
||||
|
||||
@ArrayType(type = IrisRavinePlacer.class, min = 1)
|
||||
@Desc("Define ravine placers")
|
||||
private KList<IrisRavinePlacer> ravines = new KList<>();
|
||||
|
||||
@ArrayType(type = IrisElipsoid.class, min = 1)
|
||||
@Desc("Define elipsoids")
|
||||
private KList<IrisElipsoid> elipsoids = new KList<>();
|
||||
|
||||
@ArrayType(type = IrisSphere.class, min = 1)
|
||||
@Desc("Define spheres")
|
||||
private KList<IrisSphere> spheres = new KList<>();
|
||||
|
||||
@ArrayType(type = IrisPyramid.class, min = 1)
|
||||
@Desc("Define pyramids")
|
||||
private KList<IrisPyramid> pyramids = new KList<>();
|
||||
|
||||
|
||||
@BlockCoordinates
|
||||
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) {
|
||||
doCarving(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, depth, -1);
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
public void doCarving(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
int nextRecursion = recursion + 1;
|
||||
|
||||
if (caves.isNotEmpty()) {
|
||||
for (IrisCavePlacer i : caves) {
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
i.generateCave(writer, rng, base, engine, x, y, z, nextRecursion, waterHint);
|
||||
}
|
||||
}
|
||||
|
||||
if (ravines.isNotEmpty()) {
|
||||
for (IrisRavinePlacer i : ravines) {
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
i.generateRavine(writer, rng, base, engine, x, y, z, nextRecursion, waterHint);
|
||||
}
|
||||
}
|
||||
|
||||
if (spheres.isNotEmpty()) {
|
||||
for (IrisSphere i : spheres) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
i.generate(base, engine, writer, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elipsoids.isNotEmpty()) {
|
||||
for (IrisElipsoid i : elipsoids) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
i.generate(base, engine, writer, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pyramids.isNotEmpty()) {
|
||||
for (IrisPyramid i : pyramids) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
i.generate(base, engine, writer, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxRange(IrisData data, int recursion) {
|
||||
int max = 0;
|
||||
int nextRecursion = recursion + 1;
|
||||
|
||||
for (IrisCavePlacer i : caves) {
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
max = Math.max(max, i.getSize(data, nextRecursion));
|
||||
}
|
||||
|
||||
for (IrisRavinePlacer i : ravines) {
|
||||
if (recursion > i.getMaxRecursion()) continue;
|
||||
max = Math.max(max, i.getSize(data, nextRecursion));
|
||||
}
|
||||
|
||||
if (elipsoids.isNotEmpty()) {
|
||||
max = (int) Math.max(elipsoids.stream().mapToDouble(IrisElipsoid::maxSize).max().getAsDouble(), max);
|
||||
}
|
||||
|
||||
if (spheres.isNotEmpty()) {
|
||||
max = (int) Math.max(spheres.stream().mapToDouble(IrisSphere::maxSize).max().getAsDouble(), max);
|
||||
}
|
||||
|
||||
if (pyramids.isNotEmpty()) {
|
||||
max = (int) Math.max(pyramids.stream().mapToDouble(IrisPyramid::maxSize).max().getAsDouble(), max);
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.RegistryListResource;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KSet;
|
||||
import art.arcane.volmlib.util.json.JSONObject;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Translate objects")
|
||||
@Data
|
||||
public class IrisCave extends IrisRegistrant {
|
||||
@Desc("Define the shape of this cave")
|
||||
private IrisWorm worm = new IrisWorm();
|
||||
|
||||
@Desc("Define potential forking features")
|
||||
private IrisCarving fork = new IrisCarving();
|
||||
|
||||
@RegistryListResource(IrisBiome.class)
|
||||
@Desc("Force this cave to only generate the specified custom biome")
|
||||
private String customBiome = "";
|
||||
|
||||
@Desc("Limit the worm from ever getting higher or lower than this range")
|
||||
private IrisRange verticalRange = new IrisRange(3, 255);
|
||||
|
||||
@Desc("Shape of the caves")
|
||||
private IrisCaveShape shape = new IrisCaveShape();
|
||||
|
||||
@Override
|
||||
public String getFolderName() {
|
||||
return "caves";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "Cave";
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generate(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1, true);
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) {
|
||||
double girth = getWorm().getGirth().get(base.nextParallelRNG(465156), x, z, engine.getData());
|
||||
KList<IrisPosition> points = getWorm().generate(base.nextParallelRNG(784684), engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9);
|
||||
int highestWater = Math.max(waterHint, -1);
|
||||
|
||||
if (highestWater == -1) {
|
||||
for (IrisPosition i : points) {
|
||||
double yy = i.getY() + girth;
|
||||
int th = engine.getHeight(x, z, true);
|
||||
|
||||
if (yy > th && th < engine.getDimension().getFluidHeight()) {
|
||||
highestWater = Math.max(highestWater, (int) yy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int h = Math.min(highestWater, engine.getDimension().getFluidHeight());
|
||||
|
||||
for (IrisPosition i : points) {
|
||||
fork.doCarving(writer, rng, base, engine, i.getX(), i.getY(), i.getZ(), recursion, h);
|
||||
}
|
||||
|
||||
MatterCavern c = new MatterCavern(true, customBiome, (byte) 0);
|
||||
MatterCavern w = new MatterCavern(true, customBiome, (byte) 1);
|
||||
|
||||
CNG cng = shape.getNoise(base.nextParallelRNG(8131545), engine);
|
||||
KSet<IrisPosition> mask = shape.getMasked(rng, engine);
|
||||
writer.setNoiseMasked(points,
|
||||
girth, shape.getNoiseThreshold() < 0 ? cng.noise(x, y, z) : shape.getNoiseThreshold(), cng, mask, true,
|
||||
(xf, yf, zf) -> yf <= h ? w : c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanForErrors(JSONObject p, VolmitSender sender) {
|
||||
|
||||
}
|
||||
|
||||
public int getMaxSize(IrisData data, int depth) {
|
||||
return (int) (Math.ceil(getWorm().getGirth().getMax() * 2) + getWorm().getMaxDistance() + fork.getMaxRange(data, depth));
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.*;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Snippet("cave-placer")
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Translate objects")
|
||||
@Data
|
||||
public class IrisCavePlacer implements IRare {
|
||||
private transient final AtomicCache<IrisCave> caveCache = new AtomicCache<>();
|
||||
private transient final AtomicBoolean fail = new AtomicBoolean(false);
|
||||
@Required
|
||||
@Desc("Typically a 1 in RARITY on a per chunk/fork basis")
|
||||
@MinNumber(1)
|
||||
private int rarity = 15;
|
||||
@MinNumber(1)
|
||||
@Required
|
||||
@Desc("The cave to place")
|
||||
@RegistryListResource(IrisCave.class)
|
||||
private String cave;
|
||||
@MinNumber(1)
|
||||
@MaxNumber(256)
|
||||
@Desc("The maximum recursion depth")
|
||||
private int maxRecursion = 16;
|
||||
@Desc("If set to true, this cave is allowed to break the surface")
|
||||
private boolean breakSurface = true;
|
||||
@Desc("The height range this cave can spawn at. If breakSurface is false, the output of this range will be clamped by the current world height to prevent surface breaking.")
|
||||
private IrisStyledRange caveStartHeight = new IrisStyledRange(13, 120, new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
|
||||
public IrisCave getRealCave(IrisData data) {
|
||||
return caveCache.aquire(() -> data.getCaveLoader().load(getCave()));
|
||||
}
|
||||
|
||||
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generateCave(mantle, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generateCave(MantleWriter mantle, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
if (fail.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rng.nextInt(rarity) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisData data = engine.getData();
|
||||
IrisCave cave = getRealCave(data);
|
||||
|
||||
if (cave == null) {
|
||||
Iris.warn("Unable to locate cave for generation!");
|
||||
fail.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (y == -1) {
|
||||
int h = (int) caveStartHeight.get(base, x, z, data);
|
||||
int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9);
|
||||
y = Math.min(h, ma);
|
||||
}
|
||||
|
||||
try {
|
||||
cave.generate(mantle, rng, base, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
fail.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize(IrisData data, int depth) {
|
||||
IrisCave cave = getRealCave(data);
|
||||
|
||||
if (cave != null) {
|
||||
return cave.getMaxSize(data, depth);
|
||||
}
|
||||
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
@@ -140,16 +140,19 @@ public class IrisDimension extends IrisRegistrant {
|
||||
private boolean postProcessing = true;
|
||||
@Desc("Add slabs in post processing")
|
||||
private boolean postProcessingSlabs = true;
|
||||
@Desc("Add painted walls in post processing")
|
||||
private boolean postProcessingWalls = true;
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Add painted walls in post processing")
|
||||
private boolean postProcessingWalls = true;
|
||||
@Desc("Enable or disable all carving for this dimension")
|
||||
private boolean carvingEnabled = true;
|
||||
@Desc("Profile-driven 3D cave configuration")
|
||||
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||
@Desc("forceConvertTo320Height")
|
||||
private Boolean forceConvertTo320Height = false;
|
||||
@ArrayType(type = IrisExternalDatapack.class, min = 1)
|
||||
@Desc("Pack-scoped external datapack sources for structure import and optional vanilla replacement")
|
||||
private KList<IrisExternalDatapack> externalDatapacks = new KList<>();
|
||||
@Desc("forceConvertTo320Height")
|
||||
private Boolean forceConvertTo320Height = false;
|
||||
@Desc("The world environment")
|
||||
private Environment environment = Environment.NORMAL;
|
||||
@RegistryListResource(IrisRegion.class)
|
||||
@@ -255,12 +258,10 @@ public class IrisDimension extends IrisRegistrant {
|
||||
@RegistryListResource(IrisScript.class)
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
private KList<String> dataScripts = new KList<>();
|
||||
@Desc("A list of scripts executed on chunk update\nFile extension: .update.kts")
|
||||
@RegistryListResource(IrisScript.class)
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
private KList<String> chunkUpdateScripts = new KList<>();
|
||||
@Desc("Use legacy rarity instead of modern one\nWARNING: Changing this may break expressions and image maps")
|
||||
private boolean legacyRarity = true;
|
||||
@Desc("A list of scripts executed on chunk update\nFile extension: .update.kts")
|
||||
@RegistryListResource(IrisScript.class)
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
private KList<String> chunkUpdateScripts = new KList<>();
|
||||
|
||||
public int getMaxHeight() {
|
||||
return (int) getDimensionHeight().getMax();
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.*;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.volmlib.util.matter.slices.CavernMatter;
|
||||
import lombok.Data;
|
||||
|
||||
@Snippet("carving-elipsoid")
|
||||
@Desc("Represents an procedural eliptical shape")
|
||||
@Data
|
||||
public class IrisElipsoid implements IRare {
|
||||
private transient final AtomicCache<MatterCavern> matterNodeCache = new AtomicCache<>();
|
||||
@Required
|
||||
@Desc("Typically a 1 in RARITY on a per fork basis")
|
||||
@MinNumber(1)
|
||||
private int rarity = 1;
|
||||
@RegistryListResource(IrisBiome.class)
|
||||
@Desc("Force this cave to only generate the specified custom biome")
|
||||
private String customBiome = "";
|
||||
@Desc("The styled random radius for x")
|
||||
private IrisStyledRange xRadius = new IrisStyledRange(1, 5, new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
@Desc("The styled random radius for y")
|
||||
private IrisStyledRange yRadius = new IrisStyledRange(1, 5, new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
@Desc("The styled random radius for z")
|
||||
private IrisStyledRange zRadius = new IrisStyledRange(1, 5, new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
public void generate(RNG rng, Engine engine, MantleWriter writer, int x, int y, int z) {
|
||||
writer.setElipsoid(x, y, z,
|
||||
xRadius.get(rng, z, y, engine.getData()),
|
||||
yRadius.get(rng, x, z, engine.getData()),
|
||||
zRadius.get(rng, y, x, engine.getData()), true, matterNodeCache.aquire(() -> CavernMatter.get(getCustomBiome(), 0)));
|
||||
}
|
||||
|
||||
public double maxSize() {
|
||||
return Math.max(xRadius.getMax(), Math.max(yRadius.getMax(), zRadius.getMax()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@Desc("Defines a pack-scoped external datapack source for structure import and optional vanilla replacement")
|
||||
public class IrisExternalDatapack {
|
||||
@Desc("Stable id for this external datapack entry")
|
||||
private String id = "";
|
||||
|
||||
@Desc("Datapack source URL. Modrinth version page URLs are supported.")
|
||||
private String url = "";
|
||||
|
||||
@Desc("Enable or disable this external datapack entry")
|
||||
private boolean enabled = true;
|
||||
|
||||
@Desc("If true, Iris hard-fails startup when this external datapack cannot be synced/imported/installed")
|
||||
private boolean required = false;
|
||||
|
||||
@Desc("If true, minecraft namespace worldgen assets may replace vanilla targets listed in replaceTargets")
|
||||
private boolean replaceVanilla = false;
|
||||
|
||||
@Desc("Explicit replacement targets for minecraft namespace assets")
|
||||
private IrisExternalDatapackReplaceTargets replaceTargets = new IrisExternalDatapackReplaceTargets();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.object.annotations.ArrayType;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@Desc("Explicit minecraft namespace targets that may be replaced by an external datapack")
|
||||
public class IrisExternalDatapackReplaceTargets {
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
@Desc("Structure ids that may be replaced when replaceVanilla is enabled")
|
||||
private KList<String> structures = new KList<>();
|
||||
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
@Desc("Structure set ids that may be replaced when replaceVanilla is enabled")
|
||||
private KList<String> structureSets = new KList<>();
|
||||
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
@Desc("Template pool ids that may be replaced when replaceVanilla is enabled")
|
||||
private KList<String> templatePools = new KList<>();
|
||||
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
@Desc("Processor list ids that may be replaced when replaceVanilla is enabled")
|
||||
private KList<String> processorLists = new KList<>();
|
||||
|
||||
@ArrayType(type = String.class, min = 1)
|
||||
@Desc("Biome has_structure tag ids that may be replaced when replaceVanilla is enabled")
|
||||
private KList<String> biomeHasStructureTags = new KList<>();
|
||||
|
||||
public boolean hasAnyTargets() {
|
||||
return !structures.isEmpty()
|
||||
|| !structureSets.isEmpty()
|
||||
|| !templatePools.isEmpty()
|
||||
|| !processorLists.isEmpty()
|
||||
|| !biomeHasStructureTags.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.*;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.volmlib.util.matter.slices.CavernMatter;
|
||||
import lombok.Data;
|
||||
|
||||
@Snippet("carving-pyramid")
|
||||
@Desc("Represents an procedural eliptical shape")
|
||||
@Data
|
||||
public class IrisPyramid implements IRare {
|
||||
private transient final AtomicCache<MatterCavern> matterNodeCache = new AtomicCache<>();
|
||||
@Required
|
||||
@Desc("Typically a 1 in RARITY on a per fork basis")
|
||||
@MinNumber(1)
|
||||
private int rarity = 1;
|
||||
@RegistryListResource(IrisBiome.class)
|
||||
@Desc("Force this cave to only generate the specified custom biome")
|
||||
private String customBiome = "";
|
||||
@Desc("The styled random radius for x")
|
||||
private IrisStyledRange baseWidth = new IrisStyledRange(1, 5, new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
|
||||
public void generate(RNG rng, Engine engine, MantleWriter writer, int x, int y, int z) {
|
||||
writer.setPyramid(x, y, z, matterNodeCache.aquire(() -> CavernMatter.get(getCustomBiome(), 0)),
|
||||
(int) baseWidth.get(rng, z, y, engine.getData()), true);
|
||||
}
|
||||
|
||||
public double maxSize() {
|
||||
return baseWidth.getMax();
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.MaxNumber;
|
||||
import art.arcane.iris.engine.object.annotations.MinNumber;
|
||||
import art.arcane.iris.engine.object.annotations.RegistryListResource;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.json.JSONObject;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Translate objects")
|
||||
@Data
|
||||
public class IrisRavine extends IrisRegistrant {
|
||||
@Desc("Define the shape of this ravine (2d, ignores Y)")
|
||||
private IrisWorm worm = new IrisWorm();
|
||||
|
||||
@RegistryListResource(IrisBiome.class)
|
||||
@Desc("Force this cave to only generate the specified custom biome")
|
||||
private String customBiome = "";
|
||||
|
||||
@Desc("Define potential forking features")
|
||||
private IrisCarving fork = new IrisCarving();
|
||||
|
||||
@Desc("The style used to determine the curvature of this worm's y")
|
||||
private IrisShapedGeneratorStyle depthStyle = new IrisShapedGeneratorStyle(NoiseStyle.PERLIN, 5, 18);
|
||||
|
||||
@Desc("The style used to determine the curvature of this worm's y")
|
||||
private IrisShapedGeneratorStyle baseWidthStyle = new IrisShapedGeneratorStyle(NoiseStyle.PERLIN, 3, 6);
|
||||
|
||||
@MinNumber(1)
|
||||
@MaxNumber(100)
|
||||
@Desc("The angle at which the ravine widens as it gets closer to the surface")
|
||||
private double angle = 18;
|
||||
|
||||
@MinNumber(1)
|
||||
@MaxNumber(100)
|
||||
@Desc("The angle at which the ravine widens as it gets closer to the surface")
|
||||
private double topAngle = 38;
|
||||
|
||||
@Desc("To fill this cave with lava, set the lava level to a height from the bottom most point of the cave.")
|
||||
private int lavaLevel = -1;
|
||||
|
||||
@Desc("How many worm nodes must be placed to actually generate a ravine? Higher reduces the chances but also reduces ravine 'holes'")
|
||||
private int nodeThreshold = 5;
|
||||
|
||||
@MinNumber(1)
|
||||
@MaxNumber(8)
|
||||
@Desc("The thickness of the ravine ribs")
|
||||
private double ribThickness = 3;
|
||||
|
||||
@Override
|
||||
public String getFolderName() {
|
||||
return "ravines";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "Ravine";
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generate(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generate(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
KList<IrisPosition> pos = getWorm().generate(base.nextParallelRNG(879615), engine.getData(), writer, null, x, y, z, true, 0);
|
||||
CNG dg = depthStyle.getGenerator().create(base.nextParallelRNG(7894156), engine.getData());
|
||||
CNG bw = baseWidthStyle.getGenerator().create(base.nextParallelRNG(15315456), engine.getData());
|
||||
int highestWater = Math.max(waterHint, -1);
|
||||
boolean water = false;
|
||||
|
||||
if (highestWater == -1) {
|
||||
for (IrisPosition i : pos) {
|
||||
int rsurface = y == -1 ? engine.getComplex().getHeightStream().get(x, z).intValue() : y;
|
||||
int depth = (int) Math.round(dg.fitDouble(depthStyle.getMin(), depthStyle.getMax(), i.getX(), i.getZ()));
|
||||
int surface = (int) Math.round(rsurface - depth * 0.45);
|
||||
int yy = surface + depth;
|
||||
int th = engine.getHeight(x, z, true);
|
||||
|
||||
if (yy > th && th < engine.getDimension().getFluidHeight()) {
|
||||
highestWater = Math.max(highestWater, yy);
|
||||
water = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
water = true;
|
||||
}
|
||||
|
||||
MatterCavern c = new MatterCavern(true, customBiome, (byte) (water ? 1 : 0));
|
||||
MatterCavern l = new MatterCavern(true, customBiome, (byte) 2);
|
||||
|
||||
if (pos.size() < nodeThreshold) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (IrisPosition p : pos) {
|
||||
int rsurface = y == -1 ? engine.getComplex().getHeightStream().get(x, z).intValue() : y;
|
||||
int depth = (int) Math.round(dg.fitDouble(depthStyle.getMin(), depthStyle.getMax(), p.getX(), p.getZ()));
|
||||
int width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ()));
|
||||
int surface = (int) Math.round(rsurface - depth * 0.45);
|
||||
|
||||
fork.doCarving(writer, rng, base, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, highestWater);
|
||||
|
||||
for (int i = surface + depth; i >= surface; i--) {
|
||||
if (i % ribThickness == 0) {
|
||||
double v = width + ((((surface + depth) - i) * (angle / 360D)));
|
||||
|
||||
if (v <= 0.25) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i <= ribThickness + 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (lavaLevel >= 0 && i <= lavaLevel + (surface - depthStyle.getMid())) {
|
||||
writer.setElipsoid(p.getX(), i, p.getZ(), v, ribThickness, v, true, l);
|
||||
} else {
|
||||
writer.setElipsoid(p.getX(), i, p.getZ(), v, ribThickness, v, true, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = surface - depth; i <= surface; i++) {
|
||||
if (i % ribThickness == 0) {
|
||||
double v = width - ((((surface - depth) - i) * (angle / 360D)));
|
||||
|
||||
if (v <= 0.25) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i <= ribThickness + 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (lavaLevel >= 0 && i <= lavaLevel + (surface - depthStyle.getMid())) {
|
||||
writer.setElipsoid(p.getX(), i, p.getZ(), v, ribThickness, v, true, l);
|
||||
} else {
|
||||
writer.setElipsoid(p.getX(), i, p.getZ(), v, ribThickness, v, true, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanForErrors(JSONObject p, VolmitSender sender) {
|
||||
|
||||
}
|
||||
|
||||
public int getMaxSize(IrisData data, int depth) {
|
||||
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.*;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Snippet("ravine-placer")
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Translate objects")
|
||||
@Data
|
||||
public class IrisRavinePlacer implements IRare {
|
||||
private transient final AtomicCache<IrisRavine> ravineCache = new AtomicCache<>();
|
||||
private transient final AtomicBoolean fail = new AtomicBoolean(false);
|
||||
@Required
|
||||
@Desc("Typically a 1 in RARITY on a per chunk/fork basis")
|
||||
@MinNumber(1)
|
||||
private int rarity = 15;
|
||||
@MinNumber(1)
|
||||
@Required
|
||||
@Desc("The ravine to place")
|
||||
@RegistryListResource(IrisRavine.class)
|
||||
private String ravine;
|
||||
@MinNumber(1)
|
||||
@MaxNumber(256)
|
||||
@Desc("The maximum recursion depth")
|
||||
private int maxRecursion = 100;
|
||||
|
||||
public IrisRavine getRealRavine(IrisData data) {
|
||||
return ravineCache.aquire(() -> data.getRavineLoader().load(getRavine()));
|
||||
}
|
||||
|
||||
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
|
||||
generateRavine(mantle, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1);
|
||||
}
|
||||
|
||||
public void generateRavine(MantleWriter mantle, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||
if (fail.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rng.nextInt(rarity) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisData data = engine.getData();
|
||||
IrisRavine ravine = getRealRavine(data);
|
||||
|
||||
if (ravine == null) {
|
||||
Iris.warn("Unable to locate ravine for generation!");
|
||||
fail.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int xx = x + rng.nextInt(15);
|
||||
int zz = z + rng.nextInt(15);
|
||||
ravine.generate(mantle, rng, base, engine, xx, y, zz, recursion, waterHint);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
fail.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize(IrisData data, int depth) {
|
||||
return getRealRavine(data).getMaxSize(data, depth);
|
||||
}
|
||||
}
|
||||
@@ -112,8 +112,6 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
||||
@MinNumber(0.0001)
|
||||
@Desc("How large cave biomes are in this region")
|
||||
private double caveBiomeZoom = 1;
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Profile-driven 3D cave configuration")
|
||||
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.annotations.*;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.volmlib.util.matter.slices.CavernMatter;
|
||||
import lombok.Data;
|
||||
|
||||
@Snippet("carving-sphere")
|
||||
@Desc("Represents an procedural eliptical shape")
|
||||
@Data
|
||||
public class IrisSphere implements IRare {
|
||||
private transient final AtomicCache<MatterCavern> matterNodeCache = new AtomicCache<>();
|
||||
@Required
|
||||
@Desc("Typically a 1 in RARITY on a per fork basis")
|
||||
@MinNumber(1)
|
||||
private int rarity = 1;
|
||||
@RegistryListResource(IrisBiome.class)
|
||||
@Desc("Force this cave to only generate the specified custom biome")
|
||||
private String customBiome = "";
|
||||
@Desc("The styled random radius for x")
|
||||
private IrisStyledRange radius = new IrisStyledRange(1, 5, new IrisGeneratorStyle(NoiseStyle.STATIC));
|
||||
|
||||
public void generate(RNG rng, Engine engine, MantleWriter writer, int x, int y, int z) {
|
||||
writer.setSphere(x, y, z, radius.get(rng, z, y, engine.getData()), true, matterNodeCache.aquire(() -> CavernMatter.get(getCustomBiome(), 0)));
|
||||
}
|
||||
|
||||
public double maxSize() {
|
||||
return radius.getMax();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.util.common.director.handlers;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisCave;
|
||||
import art.arcane.iris.util.common.director.specialhandlers.RegistrantHandler;
|
||||
|
||||
public class CaveHandler extends RegistrantHandler<IrisCave> {
|
||||
public CaveHandler() {
|
||||
super(IrisCave.class, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRandomDefault() {
|
||||
return "cave";
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.util.project.interpolation.InterpolationMethod;
|
||||
public enum NoiseType {
|
||||
WHITE(WhiteNoise::new),
|
||||
@@ -97,7 +96,6 @@ public enum NoiseType {
|
||||
}
|
||||
|
||||
public NoiseGenerator create(long seed) {
|
||||
if (IrisSettings.get().getGenerator().offsetNoiseTypes) return f.create(seed).offset(seed);
|
||||
else return f.create(seed);
|
||||
return f.create(seed).offset(seed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,16 +352,16 @@ public interface ProceduralStream<T> extends ProceduralLayer, Interpolated<T> {
|
||||
return new SelectionStream<V>(this, rarityTypes);
|
||||
}
|
||||
|
||||
default <V extends IRare> ProceduralStream<V> selectRarity(List<V> types, boolean legacy) {
|
||||
return IRare.stream(this.forceDouble(), types, legacy);
|
||||
default <V extends IRare> ProceduralStream<V> selectRarity(List<V> types) {
|
||||
return IRare.stream(this.forceDouble(), types);
|
||||
}
|
||||
|
||||
default <V> ProceduralStream<IRare> selectRarity(List<V> types, Function<V, IRare> loader, boolean legacy) {
|
||||
default <V> ProceduralStream<IRare> selectRarity(List<V> types, Function<V, IRare> loader) {
|
||||
List<IRare> r = new ArrayList<>();
|
||||
for (V f : types) {
|
||||
r.add(loader.apply(f));
|
||||
}
|
||||
return selectRarity(r, legacy);
|
||||
return selectRarity(r);
|
||||
}
|
||||
|
||||
default <V> int countPossibilities(List<V> types, Function<V, IRare> loader) {
|
||||
|
||||
Reference in New Issue
Block a user