mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-19 15:09:18 +00:00
Allow placing datapack structures in Iris worlds (#1225)
* Allow placing datapack structures in Iris worlds * command for generating configs for datapack structures * remove the sub dir from the snippet key
This commit is contained in:
@@ -83,6 +83,7 @@ dependencies {
|
|||||||
slim(libs.commons.io)
|
slim(libs.commons.io)
|
||||||
slim(libs.commons.lang)
|
slim(libs.commons.lang)
|
||||||
slim(libs.commons.lang3)
|
slim(libs.commons.lang3)
|
||||||
|
slim(libs.commons.math3)
|
||||||
slim(libs.oshi)
|
slim(libs.oshi)
|
||||||
slim(libs.lz4)
|
slim(libs.lz4)
|
||||||
slim(libs.fastutil)
|
slim(libs.fastutil)
|
||||||
|
|||||||
@@ -18,19 +18,29 @@
|
|||||||
|
|
||||||
package com.volmit.iris.core.commands;
|
package com.volmit.iris.core.commands;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.ServerConfigurator;
|
import com.volmit.iris.core.ServerConfigurator;
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
|
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.context.IrisContext;
|
import com.volmit.iris.util.context.IrisContext;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||||
import com.volmit.iris.util.decree.annotations.Decree;
|
import com.volmit.iris.util.decree.annotations.Decree;
|
||||||
import com.volmit.iris.util.decree.annotations.Param;
|
import com.volmit.iris.util.decree.annotations.Param;
|
||||||
|
import com.volmit.iris.util.decree.specialhandlers.NullableDimensionHandler;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||||
@@ -42,6 +52,7 @@ import com.volmit.iris.util.nbt.mca.MCAFile;
|
|||||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
import net.jpountz.lz4.LZ4FrameInputStream;
|
||||||
@@ -59,6 +70,8 @@ import java.util.*;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
@@ -143,6 +156,130 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Decree(description = "Generate Iris structures for all loaded datapack structures")
|
||||||
|
public void generateStructures(
|
||||||
|
@Param(description = "The pack to add the generated structures to", aliases = "pack", defaultValue = "---", customHandler = NullableDimensionHandler.class)
|
||||||
|
IrisDimension dimension,
|
||||||
|
@Param(description = "Ignore existing structures", defaultValue = "false")
|
||||||
|
boolean force
|
||||||
|
) {
|
||||||
|
var map = INMS.get().collectStructures();
|
||||||
|
if (map.isEmpty()) {
|
||||||
|
sender().sendMessage(C.IRIS + "No structures found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sender().sendMessage(C.IRIS + "Found " + map.size() + " structures");
|
||||||
|
|
||||||
|
final File dataDir;
|
||||||
|
final IrisData data;
|
||||||
|
final Set<String> existingStructures;
|
||||||
|
final Map<String, Set<String>> snippets;
|
||||||
|
final File dimensionFile;
|
||||||
|
final File structuresFolder;
|
||||||
|
final File snippetsFolder;
|
||||||
|
|
||||||
|
var dimensionObj = new JsonObject();
|
||||||
|
|
||||||
|
if (dimension == null) {
|
||||||
|
dataDir = Iris.instance.getDataFolder("structures");
|
||||||
|
IO.delete(dataDir);
|
||||||
|
data = IrisData.get(dataDir);
|
||||||
|
existingStructures = Set.of();
|
||||||
|
snippets = Map.of();
|
||||||
|
dimensionFile = new File(dataDir, "structures.json");
|
||||||
|
} else {
|
||||||
|
data = dimension.getLoader();
|
||||||
|
dataDir = data.getDataFolder();
|
||||||
|
existingStructures = new KSet<>(data.getJigsawStructureLoader().getPossibleKeys());
|
||||||
|
|
||||||
|
dimensionObj = data.getGson().fromJson(IO.readAll(dimension.getLoadFile()), JsonObject.class);
|
||||||
|
snippets = Optional.ofNullable(dimensionObj.getAsJsonArray("jigsawStructures"))
|
||||||
|
.map(array -> array.asList()
|
||||||
|
.stream()
|
||||||
|
.filter(JsonElement::isJsonPrimitive)
|
||||||
|
.collect(Collectors.toMap(element -> data.getGson()
|
||||||
|
.fromJson(element, IrisJigsawStructurePlacement.class)
|
||||||
|
.getStructure(),
|
||||||
|
element -> Set.of(element.getAsString()),
|
||||||
|
KSet::merge)))
|
||||||
|
.orElse(Map.of());
|
||||||
|
|
||||||
|
dimensionFile = dimension.getLoadFile();
|
||||||
|
}
|
||||||
|
structuresFolder = new File(dataDir, "jigsaw-structures");
|
||||||
|
snippetsFolder = new File(dataDir, "snippet" + "/" + IrisJigsawStructurePlacement.class.getAnnotation(Snippet.class).value());
|
||||||
|
|
||||||
|
var gson = data.getGson();
|
||||||
|
var jigsawStructures = Optional.ofNullable(dimensionObj.getAsJsonArray("jigsawStructures"))
|
||||||
|
.orElse(new JsonArray(map.size()));
|
||||||
|
|
||||||
|
map.forEach((key, placement) -> {
|
||||||
|
String loadKey = "datapack/" + key.namespace() + "/" + key.key();
|
||||||
|
if (existingStructures.contains(loadKey) && !force)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var structures = placement.structures();
|
||||||
|
var obj = placement.toJson(loadKey);
|
||||||
|
if (obj == null || structures.isEmpty()) {
|
||||||
|
sender().sendMessage(C.RED + "Failed to generate hook for " + key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File snippetFile = new File(snippetsFolder, loadKey + ".json");
|
||||||
|
try {
|
||||||
|
IO.writeAll(snippetFile, gson.toJson(obj));
|
||||||
|
} catch (IOException e) {
|
||||||
|
sender().sendMessage(C.RED + "Failed to generate snippet for " + key);
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> loadKeys = snippets.getOrDefault(loadKey, Set.of(loadKey));
|
||||||
|
jigsawStructures.asList().removeIf(e -> loadKeys.contains((e.isJsonObject() ? e.getAsJsonObject().get("structure") : e).getAsString()));
|
||||||
|
jigsawStructures.add("snippet/" + loadKey);
|
||||||
|
|
||||||
|
String structureKey;
|
||||||
|
if (structures.size() > 1) {
|
||||||
|
KList<String> common = new KList<>();
|
||||||
|
for (int i = 0; i < structures.size(); i++) {
|
||||||
|
var tags = structures.get(i).tags();
|
||||||
|
if (i == 0) common.addAll(tags);
|
||||||
|
else common.removeIf(tag -> !tags.contains(tag));
|
||||||
|
}
|
||||||
|
structureKey = common.isNotEmpty() ? "#" + common.getFirst() : structures.getFirst().key();
|
||||||
|
} else structureKey = structures.getFirst().key();
|
||||||
|
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
if (structures.size() > 1) {
|
||||||
|
structures.stream()
|
||||||
|
.flatMap(structure -> {
|
||||||
|
String[] arr = new String[structure.weight()];
|
||||||
|
Arrays.fill(arr, structure.key());
|
||||||
|
return Arrays.stream(arr);
|
||||||
|
})
|
||||||
|
.forEach(array::add);
|
||||||
|
} else array.add(structureKey);
|
||||||
|
|
||||||
|
obj = new JsonObject();
|
||||||
|
obj.addProperty("structureKey", structureKey);
|
||||||
|
obj.add("datapackStructures", array);
|
||||||
|
|
||||||
|
File out = new File(structuresFolder, loadKey + ".json");
|
||||||
|
out.getParentFile().mkdirs();
|
||||||
|
try {
|
||||||
|
IO.writeAll(out, gson.toJson(obj));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dimensionObj.add("jigsawStructures", jigsawStructures);
|
||||||
|
IO.writeAll(dimensionFile, gson.toJson(dimensionObj));
|
||||||
|
|
||||||
|
data.hotloaded();
|
||||||
|
}
|
||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
public void packBenchmark(
|
public void packBenchmark(
|
||||||
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
|
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
|
||||||
|
|||||||
@@ -425,6 +425,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String snippetType = typeToken.getRawType().getDeclaredAnnotation(Snippet.class).value();
|
String snippetType = typeToken.getRawType().getDeclaredAnnotation(Snippet.class).value();
|
||||||
|
String snippedBase = "snippet/" + snippetType + "/";
|
||||||
|
|
||||||
return new TypeAdapter<>() {
|
return new TypeAdapter<>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -438,10 +439,12 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
|
|
||||||
if (reader.peek().equals(JsonToken.STRING)) {
|
if (reader.peek().equals(JsonToken.STRING)) {
|
||||||
String r = reader.nextString();
|
String r = reader.nextString();
|
||||||
|
if (!r.startsWith("snippet/"))
|
||||||
|
return null;
|
||||||
|
if (!r.startsWith(snippedBase))
|
||||||
|
r = snippedBase + r.substring(8);
|
||||||
|
|
||||||
if (r.startsWith("snippet/" + snippetType + "/")) {
|
|
||||||
File f = new File(getDataFolder(), r + ".json");
|
File f = new File(getDataFolder(), r + ".json");
|
||||||
|
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
|
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
|
||||||
return adapter.read(snippetReader);
|
return adapter.read(snippetReader);
|
||||||
@@ -451,7 +454,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
} else {
|
} else {
|
||||||
Iris.error("Couldn't find snippet " + r + " in " + reader.getPath());
|
Iris.error("Couldn't find snippet " + r + " in " + reader.getPath());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -488,7 +490,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
.map(s -> s.substring(absPath.length() + 1))
|
.map(s -> s.substring(absPath.length() + 1))
|
||||||
.map(s -> s.replace("\\", "/"))
|
.map(s -> s.replace("\\", "/"))
|
||||||
.map(s -> s.split("\\Q.\\E")[0])
|
.map(s -> s.split("\\Q.\\E")[0])
|
||||||
.forEach(s -> l.add("snippet/" + f + "/" + s));
|
.forEach(s -> l.add("snippet/" + s));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.volmit.iris.util.collection.KList;
|
|||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -39,6 +40,7 @@ public abstract class IrisRegistrant {
|
|||||||
@ArrayType(min = 1, type = String.class)
|
@ArrayType(min = 1, type = String.class)
|
||||||
private KList<String> preprocessors = new KList<>();
|
private KList<String> preprocessors = new KList<>();
|
||||||
|
|
||||||
|
@EqualsAndHashCode.Exclude
|
||||||
private transient IrisData loader;
|
private transient IrisData loader;
|
||||||
|
|
||||||
private transient String loadKey;
|
private transient String loadKey;
|
||||||
|
|||||||
@@ -18,8 +18,12 @@
|
|||||||
|
|
||||||
package com.volmit.iris.core.nms;
|
package com.volmit.iris.core.nms;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
@@ -136,4 +140,8 @@ public interface INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KMap<Material, List<BlockProperty>> getBlockProperties();
|
KMap<Material, List<BlockProperty>> getBlockProperties();
|
||||||
|
|
||||||
|
void placeStructures(Chunk chunk);
|
||||||
|
|
||||||
|
KMap<Identifier, StructurePlacement> collectStructures();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.volmit.iris.core.nms.container;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement.SpreadType;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import org.apache.commons.math3.fraction.Fraction;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@Accessors(fluent = true, chain = true)
|
||||||
|
public abstract class StructurePlacement {
|
||||||
|
private final int salt;
|
||||||
|
private final float frequency;
|
||||||
|
private final List<Structure> structures;
|
||||||
|
|
||||||
|
public abstract JsonObject toJson(String structure);
|
||||||
|
|
||||||
|
protected JsonObject createBase(String structure) {
|
||||||
|
JsonObject object = new JsonObject();
|
||||||
|
object.addProperty("structure", structure);
|
||||||
|
object.addProperty("salt", salt);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int frequencyToSpacing() {
|
||||||
|
var frac = new Fraction(Math.max(Math.min(frequency, 1), 0.000000001f));
|
||||||
|
return (int) Math.round(Math.sqrt((double) frac.getDenominator() / frac.getNumerator()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Accessors(chain = true, fluent = true)
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@SuperBuilder
|
||||||
|
public static class RandomSpread extends StructurePlacement {
|
||||||
|
private final int spacing;
|
||||||
|
private final int separation;
|
||||||
|
private final SpreadType spreadType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObject toJson(String structure) {
|
||||||
|
JsonObject object = createBase(structure);
|
||||||
|
object.addProperty("spacing", Math.max(spacing, frequencyToSpacing()));
|
||||||
|
object.addProperty("separation", separation);
|
||||||
|
object.addProperty("spreadType", spreadType.name());
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@SuperBuilder
|
||||||
|
public static class ConcentricRings extends StructurePlacement {
|
||||||
|
private final int distance;
|
||||||
|
private final int spread;
|
||||||
|
private final int count;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObject toJson(String structure) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Structure(
|
||||||
|
int weight,
|
||||||
|
String key,
|
||||||
|
List<String> tags
|
||||||
|
) {
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return weight > 0 && key != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,10 +19,13 @@
|
|||||||
package com.volmit.iris.core.nms.v1X;
|
package com.volmit.iris.core.nms.v1X;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
@@ -135,6 +138,16 @@ public class NMSBinding1X implements INMSBinding {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
return new KMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
public CompoundTag serializeEntity(Entity location) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
|
|
||||||
@BlockCoordinates
|
@BlockCoordinates
|
||||||
private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng, boolean forcePlace) {
|
private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng, boolean forcePlace) {
|
||||||
|
if (structure == null || structure.getDatapackStructures().isNotEmpty()) return false;
|
||||||
return new PlannedStructure(structure, position, rng, forcePlace).place(writer, getMantle(), writer.getEngine());
|
return new PlannedStructure(structure, position, rng, forcePlace).place(writer, getMantle(), writer.getEngine());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +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 com.volmit.iris.engine.object;
|
|
||||||
|
|
||||||
import com.volmit.iris.engine.object.annotations.*;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
@Snippet("jigsaw-placer")
|
|
||||||
@Accessors(chain = true)
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Desc("Represents a jigsaw placement")
|
|
||||||
@Data
|
|
||||||
public class IrisJigsawPlacement {
|
|
||||||
@RegistryListResource(IrisJigsawStructure.class)
|
|
||||||
@Required
|
|
||||||
@Desc("The jigsaw structure to use")
|
|
||||||
private String structure = "";
|
|
||||||
|
|
||||||
@Required
|
|
||||||
@MinNumber(1)
|
|
||||||
@Desc("The rarity for this jigsaw structure to place on a per chunk basis")
|
|
||||||
private int rarity = 29;
|
|
||||||
}
|
|
||||||
@@ -23,6 +23,7 @@ import com.volmit.iris.core.loader.IrisRegistrant;
|
|||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.object.annotations.*;
|
import com.volmit.iris.engine.object.annotations.*;
|
||||||
import com.volmit.iris.engine.object.annotations.functions.StructureKeyFunction;
|
import com.volmit.iris.engine.object.annotations.functions.StructureKeyFunction;
|
||||||
|
import com.volmit.iris.engine.object.annotations.functions.StructureKeyOrTagFunction;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
@@ -40,6 +41,11 @@ import lombok.experimental.Accessors;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class IrisJigsawStructure extends IrisRegistrant {
|
public class IrisJigsawStructure extends IrisRegistrant {
|
||||||
|
@RegistryListFunction(StructureKeyFunction.class)
|
||||||
|
@ArrayType(min = 1, type = String.class)
|
||||||
|
@Desc("The datapack structures. Randomply chooses a structure to place\nIgnores every other setting")
|
||||||
|
private KList<String> datapackStructures = new KList<>();
|
||||||
|
|
||||||
@RegistryListResource(IrisJigsawPiece.class)
|
@RegistryListResource(IrisJigsawPiece.class)
|
||||||
@Required
|
@Required
|
||||||
@ArrayType(min = 1, type = String.class)
|
@ArrayType(min = 1, type = String.class)
|
||||||
@@ -70,7 +76,7 @@ public class IrisJigsawStructure extends IrisRegistrant {
|
|||||||
@Desc("Set to true to prevent rotating the initial structure piece")
|
@Desc("Set to true to prevent rotating the initial structure piece")
|
||||||
private boolean disableInitialRotation = false;
|
private boolean disableInitialRotation = false;
|
||||||
|
|
||||||
@RegistryListFunction(StructureKeyFunction.class)
|
@RegistryListFunction(StructureKeyOrTagFunction.class)
|
||||||
@Desc("The minecraft key to use when creating treasure maps")
|
@Desc("The minecraft key to use when creating treasure maps")
|
||||||
private String structureKey = null;
|
private String structureKey = null;
|
||||||
|
|
||||||
@@ -117,6 +123,10 @@ public class IrisJigsawStructure extends IrisRegistrant {
|
|||||||
|
|
||||||
public int getMaxDimension() {
|
public int getMaxDimension() {
|
||||||
return maxDimension.aquire(() -> {
|
return maxDimension.aquire(() -> {
|
||||||
|
if (datapackStructures.isNotEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (useMaxPieceSizeForParallaxRadius) {
|
if (useMaxPieceSizeForParallaxRadius) {
|
||||||
int max = 0;
|
int max = 0;
|
||||||
KList<String> pools = new KList<>();
|
KList<String> pools = new KList<>();
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.framework.EngineAssignedComponent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import com.volmit.iris.util.math.RNG;
|
||||||
|
import com.volmit.iris.util.noise.CNG;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
public class IrisStructurePopulator extends EngineAssignedComponent {
|
||||||
|
private final CNG cng;
|
||||||
|
private final long mantle;
|
||||||
|
private final long jigsaw;
|
||||||
|
|
||||||
|
public IrisStructurePopulator(Engine engine) {
|
||||||
|
super(engine, "Datapack Structures");
|
||||||
|
mantle = engine.getSeedManager().getMantle();
|
||||||
|
jigsaw = engine.getSeedManager().getJigsaw();
|
||||||
|
cng = NoiseStyle.STATIC.create(new RNG(jigsaw));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public void populateStructures(int x, int z, BiPredicate<String, Boolean> placer) {
|
||||||
|
int bX = x << 4, bZ = z << 4;
|
||||||
|
var dimension = getDimension();
|
||||||
|
var region = getEngine().getRegion(bX + 8, bZ + 8);
|
||||||
|
var biome = getEngine().getSurfaceBiome(bX + 8, bZ + 8);
|
||||||
|
var loader = getData().getJigsawStructureLoader();
|
||||||
|
|
||||||
|
long seed = cng.fit(Integer.MIN_VALUE, Integer.MAX_VALUE, x, z);
|
||||||
|
if (dimension.getStronghold() != null) {
|
||||||
|
var list = getDimension().getStrongholds(mantle);
|
||||||
|
if (list != null && list.contains(new Position2(bX, bZ))) {
|
||||||
|
place(placer, loader.load(dimension.getStronghold()), new RNG(seed), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean placed = place(placer, biome.getJigsawStructures(), seed, x, z);
|
||||||
|
if (!placed) placed = place(placer, region.getJigsawStructures(), seed, x, z);
|
||||||
|
if (!placed) place(placer, dimension.getJigsawStructures(), seed, x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean place(BiPredicate<String, Boolean> placer, KList<IrisJigsawStructurePlacement> placements, long seed, int x, int z) {
|
||||||
|
var placement = pick(placements, seed, x, z);
|
||||||
|
if (placement == null) return false;
|
||||||
|
return place(placer, getData().getJigsawStructureLoader().load(placement.getStructure()), new RNG(seed), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@ChunkCoordinates
|
||||||
|
private IrisJigsawStructurePlacement pick(List<IrisJigsawStructurePlacement> structures, long seed, int x, int z) {
|
||||||
|
return IRare.pick(structures.stream()
|
||||||
|
.filter(p -> p.shouldPlace(getData(), getDimension().getJigsawStructureDivisor(), jigsaw, x, z))
|
||||||
|
.toList(), new RNG(seed).nextDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
private boolean place(BiPredicate<String, Boolean> placer, IrisJigsawStructure structure, RNG rng, boolean ignoreBiomes) {
|
||||||
|
if (structure == null || structure.getDatapackStructures().isEmpty()) return false;
|
||||||
|
var keys = structure.getDatapackStructures().shuffleCopy(rng);
|
||||||
|
while (keys.isNotEmpty()) {
|
||||||
|
String key = keys.removeFirst();
|
||||||
|
if (key != null && placer.test(key, ignoreBiomes || structure.isForcePlace()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,6 @@ public class StructureKeyFunction implements ListFunction<KList<String>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KList<String> apply(IrisData irisData) {
|
public KList<String> apply(IrisData irisData) {
|
||||||
return INMS.get().getStructureKeys();
|
return INMS.get().getStructureKeys().removeWhere(t -> t.startsWith("#"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.volmit.iris.engine.object.annotations.functions;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.core.nms.INMS;
|
||||||
|
import com.volmit.iris.engine.framework.ListFunction;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
|
||||||
|
public class StructureKeyOrTagFunction implements ListFunction<KList<String>> {
|
||||||
|
@Override
|
||||||
|
public String key() {
|
||||||
|
return "structure-key-or-tag";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String fancyName() {
|
||||||
|
return "Structure Key or Tag";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KList<String> apply(IrisData irisData) {
|
||||||
|
return INMS.get().getStructureKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -240,6 +240,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
}
|
}
|
||||||
}, syncExecutor));
|
}, syncExecutor));
|
||||||
}
|
}
|
||||||
|
futures.add(CompletableFuture.runAsync(() -> INMS.get().placeStructures(c), syncExecutor));
|
||||||
|
|
||||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||||
.thenRunAsync(() -> {
|
.thenRunAsync(() -> {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.AbstractSet;
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@@ -30,13 +31,15 @@ public class KSet<T> extends AbstractSet<T> implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final ConcurrentHashMap<T, Boolean> map;
|
private final ConcurrentHashMap<T, Boolean> map;
|
||||||
|
|
||||||
public KSet() {
|
public KSet(Collection<? extends T> c) {
|
||||||
map = new ConcurrentHashMap<>();
|
this(c.size());
|
||||||
|
addAll(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KSet(Collection<? extends T> c) {
|
@SafeVarargs
|
||||||
this();
|
public KSet(T... values) {
|
||||||
addAll(c);
|
this(values.length);
|
||||||
|
addAll(Arrays.asList(values));
|
||||||
}
|
}
|
||||||
|
|
||||||
public KSet(int initialCapacity, float loadFactor) {
|
public KSet(int initialCapacity, float loadFactor) {
|
||||||
@@ -47,6 +50,13 @@ public class KSet<T> extends AbstractSet<T> implements Serializable {
|
|||||||
map = new ConcurrentHashMap<>(initialCapacity);
|
map = new ConcurrentHashMap<>(initialCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> KSet<T> merge(Collection<? extends T> first, Collection<? extends T> second) {
|
||||||
|
var set = new KSet<T>();
|
||||||
|
set.addAll(first);
|
||||||
|
set.addAll(second);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return map.size();
|
return map.size();
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.volmit.iris.util.decree.specialhandlers;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.decree.DecreeParameterHandler;
|
||||||
|
import com.volmit.iris.util.decree.exceptions.DecreeParsingException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class NullableDimensionHandler implements DecreeParameterHandler<IrisDimension> {
|
||||||
|
@Override
|
||||||
|
public KList<IrisDimension> getPossibilities() {
|
||||||
|
KMap<String, IrisDimension> p = new KMap<>();
|
||||||
|
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
for (File i : Iris.instance.getDataFolder("packs").listFiles()) {
|
||||||
|
if (i.isDirectory()) {
|
||||||
|
IrisData data = IrisData.get(i);
|
||||||
|
for (IrisDimension j : data.getDimensionLoader().loadAll(data.getDimensionLoader().getPossibleKeys())) {
|
||||||
|
p.putIfAbsent(j.getLoadKey(), j);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.v();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(IrisDimension dim) {
|
||||||
|
return dim.getLoadKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IrisDimension parse(String in, boolean force) throws DecreeParsingException {
|
||||||
|
if (in.equalsIgnoreCase("---"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (in.equalsIgnoreCase("default")) {
|
||||||
|
return parse(IrisSettings.get().getGenerator().getDefaultWorldType());
|
||||||
|
}
|
||||||
|
|
||||||
|
KList<IrisDimension> options = getPossibilities(in);
|
||||||
|
|
||||||
|
|
||||||
|
if (options.isEmpty()) {
|
||||||
|
throw new DecreeParsingException("Unable to find Dimension \"" + in + "\"");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return options.stream().filter((i) -> toString(i).equalsIgnoreCase(in)).toList().get(0);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new DecreeParsingException("Unable to filter which Dimension \"" + in + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> type) {
|
||||||
|
return type.equals(IrisDimension.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRandomDefault() {
|
||||||
|
return "dimension";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ sentry = "8.14.0" # https://github.com/getsentry/sentry-java
|
|||||||
commons-io = "2.19.0" # https://central.sonatype.com/artifact/commons-io/commons-io
|
commons-io = "2.19.0" # https://central.sonatype.com/artifact/commons-io/commons-io
|
||||||
commons-lang = "2.6" # https://central.sonatype.com/artifact/commons-lang/commons-lang
|
commons-lang = "2.6" # https://central.sonatype.com/artifact/commons-lang/commons-lang
|
||||||
commons-lang3 = "3.17.0" # https://central.sonatype.com/artifact/org.apache.commons/commons-lang3
|
commons-lang3 = "3.17.0" # https://central.sonatype.com/artifact/org.apache.commons/commons-lang3
|
||||||
|
commons-math3 = "3.6.1"
|
||||||
oshi = "6.8.2" # https://central.sonatype.com/artifact/com.github.oshi/oshi-core
|
oshi = "6.8.2" # https://central.sonatype.com/artifact/com.github.oshi/oshi-core
|
||||||
fastutil = "8.5.16" # https://central.sonatype.com/artifact/it.unimi.dsi/fastutil
|
fastutil = "8.5.16" # https://central.sonatype.com/artifact/it.unimi.dsi/fastutil
|
||||||
lz4 = "1.8.0" # https://central.sonatype.com/artifact/org.lz4/lz4-java
|
lz4 = "1.8.0" # https://central.sonatype.com/artifact/org.lz4/lz4-java
|
||||||
@@ -75,6 +76,7 @@ sentry = { module = "io.sentry:sentry", version.ref = "sentry" }
|
|||||||
commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" }
|
commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" }
|
||||||
commons-lang = { module = "commons-lang:commons-lang", version.ref = "commons-lang" }
|
commons-lang = { module = "commons-lang:commons-lang", version.ref = "commons-lang" }
|
||||||
commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons-lang3" }
|
commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons-lang3" }
|
||||||
|
commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" }
|
||||||
oshi = { module = "com.github.oshi:oshi-core", version.ref = "oshi" }
|
oshi = { module = "com.github.oshi:oshi-core", version.ref = "oshi" }
|
||||||
lz4 = { module = "org.lz4:lz4-java", version.ref = "lz4" }
|
lz4 = { module = "org.lz4:lz4-java", version.ref = "lz4" }
|
||||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
import net.minecraft.core.BlockPos;
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.core.HolderSet;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -27,45 +29,55 @@ import net.minecraft.tags.StructureTags;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_20_R1.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R1.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -182,8 +194,58 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.registryOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var structure = registry.getOptional(loc).orElse(null);
|
||||||
|
if (structure == null) return false;
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
(biome) -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure, registryAccess),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -201,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -213,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSection());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().registryOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
Structure structure = list.get(j);
|
||||||
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.POST_FEATURES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinBuildHeight() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxBuildHeight() - 1;
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -236,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinBuildHeight() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinBuildHeight(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -251,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ package com.volmit.iris.core.nms.v1_20_R1;
|
|||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import com.volmit.iris.util.agent.Agent;
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -59,6 +62,8 @@ import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
|||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -79,7 +84,6 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -90,6 +94,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
|
|
||||||
@@ -704,6 +709,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.registryKeySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::getHolder)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
import net.minecraft.core.BlockPos;
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.core.HolderSet;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -27,45 +29,55 @@ import net.minecraft.tags.StructureTags;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_20_R2.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R2.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -182,8 +194,58 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.registryOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var structure = registry.getOptional(loc).orElse(null);
|
||||||
|
if (structure == null) return false;
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
(biome) -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure, registryAccess),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -201,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -213,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSection());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().registryOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
Structure structure = list.get(j);
|
||||||
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.POST_FEATURES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinBuildHeight() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxBuildHeight() - 1;
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -236,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinBuildHeight() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinBuildHeight(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -251,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
package com.volmit.iris.core.nms.v1_20_R2;
|
package com.volmit.iris.core.nms.v1_20_R2;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
@@ -21,6 +38,8 @@ import com.volmit.iris.util.matter.MatterBiomeInject;
|
|||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
@@ -58,6 +77,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -707,6 +728,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.registryKeySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::getHolder)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
import net.minecraft.core.BlockPos;
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.core.HolderSet;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -27,45 +29,55 @@ import net.minecraft.tags.StructureTags;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R3.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -182,8 +194,58 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.registryOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var structure = registry.getOptional(loc).orElse(null);
|
||||||
|
if (structure == null) return false;
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
(biome) -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -201,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -213,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSection());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().registryOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
Structure structure = list.get(j);
|
||||||
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.POST_FEATURES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinBuildHeight() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxBuildHeight() - 1;
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -236,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinBuildHeight() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinBuildHeight(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -251,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
package com.volmit.iris.core.nms.v1_20_R3;
|
package com.volmit.iris.core.nms.v1_20_R3;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
@@ -21,6 +38,8 @@ import com.volmit.iris.util.matter.MatterBiomeInject;
|
|||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
@@ -58,6 +77,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -708,6 +729,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.registryKeySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::getHolder)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
import net.minecraft.core.BlockPos;
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.core.HolderSet;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -27,43 +29,53 @@ import net.minecraft.tags.StructureTags;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_20_R4.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -180,8 +192,59 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.registryOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var holder = registry.getHolder(loc).orElse(null);
|
||||||
|
if (holder == null) return false;
|
||||||
|
var structure = holder.value();
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
(biome) -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -199,11 +262,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -211,7 +269,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSection());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().registryOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
Structure structure = list.get(j);
|
||||||
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.POST_FEATURES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinBuildHeight() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxBuildHeight() - 1;
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -234,9 +360,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinBuildHeight() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinBuildHeight(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -249,7 +388,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
package com.volmit.iris.core.nms.v1_20_R4;
|
package com.volmit.iris.core.nms.v1_20_R4;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
@@ -21,6 +35,7 @@ import com.volmit.iris.util.math.Vector3d;
|
|||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
@@ -59,6 +74,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -726,6 +743,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::getHolder)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
import net.minecraft.core.BlockPos;
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.core.HolderSet;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -27,44 +29,54 @@ import net.minecraft.tags.StructureTags;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_21_R1.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R1.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -181,8 +193,59 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.registryOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var holder = registry.getHolder(loc).orElse(null);
|
||||||
|
if (holder == null) return false;
|
||||||
|
var structure = holder.value();
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
(biome) -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -200,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -212,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSection());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().registryOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
Structure structure = list.get(j);
|
||||||
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinBuildHeight() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxBuildHeight() - 1;
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -235,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinBuildHeight() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinBuildHeight(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -250,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -1,12 +1,31 @@
|
|||||||
package com.volmit.iris.core.nms.v1_21_R1;
|
package com.volmit.iris.core.nms.v1_21_R1;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
@@ -63,6 +82,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -737,6 +758,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::getHolder)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
import net.minecraft.core.BlockPos;
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.core.HolderSet;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -27,43 +29,54 @@ import net.minecraft.tags.StructureTags;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
|
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_21_R2.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_21_R2.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R2.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -180,8 +193,59 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var holder = registry.get(loc).orElse(null);
|
||||||
|
if (holder == null) return false;
|
||||||
|
var structure = holder.value();
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
(biome) -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -199,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -211,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < list.size(); j++) {
|
||||||
|
Structure structure = list.get(j);
|
||||||
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinY() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxY();
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -234,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinY(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -249,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
package com.volmit.iris.core.nms.v1_21_R2;
|
package com.volmit.iris.core.nms.v1_21_R2;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
@@ -21,6 +36,7 @@ import com.volmit.iris.util.matter.MatterBiomeInject;
|
|||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
@@ -60,6 +76,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -734,6 +752,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::get)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
|
import net.minecraft.CrashReport;
|
||||||
|
import net.minecraft.CrashReportCategory;
|
||||||
|
import net.minecraft.ReportedException;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
@@ -26,38 +31,49 @@ import net.minecraft.util.random.WeightedRandomList;
|
|||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.*;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.biome.*;
|
import net.minecraft.world.level.biome.*;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_21_R3.CraftWorld;
|
import org.bukkit.craftbukkit.v1_21_R3.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_21_R3.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_21_R3.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R3.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
import org.spigotmc.SpigotWorldConfig;
|
import org.spigotmc.SpigotWorldConfig;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Objects;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -174,8 +190,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var holder = registry.get(loc).orElse(null);
|
||||||
|
if (holder == null) return false;
|
||||||
|
var structure = holder.value();
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
holder,
|
||||||
|
levelKey,
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
biome -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -208,11 +277,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -220,7 +284,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -230,22 +294,70 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
for (int j = 0; j < list.size(); j++) {
|
||||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
Structure structure = list.get(j);
|
||||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||||
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
}
|
||||||
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinY() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxY();
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -263,9 +375,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinY(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,7 +419,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ package com.volmit.iris.core.nms.v1_21_R3;
|
|||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import com.volmit.iris.util.agent.Agent;
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -61,6 +66,9 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -89,10 +97,16 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
@@ -179,14 +193,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(value = "null, _, _ -> null", pure = true)
|
@Contract(value = "null, _, _ -> null", pure = true)
|
||||||
private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) {
|
private Object convertFromTag(Tag tag, int depth, int maxDepth) {
|
||||||
if (tag == null || depth > maxDepth) return null;
|
if (tag == null || depth > maxDepth) return null;
|
||||||
return switch (tag) {
|
return switch (tag) {
|
||||||
case CollectionTag<?> collection -> {
|
case CollectionTag<?> collection -> {
|
||||||
KList<Object> list = new KList<>();
|
KList<Object> list = new KList<>();
|
||||||
|
|
||||||
for (Object i : collection) {
|
for (Object i : collection) {
|
||||||
if (i instanceof net.minecraft.nbt.Tag t)
|
if (i instanceof Tag t)
|
||||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
list.add(convertFromTag(t, depth + 1, maxDepth));
|
||||||
else list.add(i);
|
else list.add(i);
|
||||||
}
|
}
|
||||||
@@ -243,7 +257,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
yield tag;
|
yield tag;
|
||||||
}
|
}
|
||||||
case List<?> list -> {
|
case List<?> list -> {
|
||||||
var tag = new net.minecraft.nbt.ListTag();
|
var tag = new ListTag();
|
||||||
for (var i : list) {
|
for (var i : list) {
|
||||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
tag.add(convertToTag(i, depth + 1, maxDepth));
|
||||||
}
|
}
|
||||||
@@ -497,13 +511,13 @@ public class NMSBinding implements INMSBinding {
|
|||||||
@Override
|
@Override
|
||||||
public MCAPaletteAccess createPalette() {
|
public MCAPaletteAccess createPalette() {
|
||||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
||||||
Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
Field cf = IdMapper.class.getDeclaredField("tToId");
|
||||||
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
Field df = IdMapper.class.getDeclaredField("idToT");
|
||||||
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
Field bf = IdMapper.class.getDeclaredField("nextId");
|
||||||
cf.setAccessible(true);
|
cf.setAccessible(true);
|
||||||
df.setAccessible(true);
|
df.setAccessible(true);
|
||||||
bf.setAccessible(true);
|
bf.setAccessible(true);
|
||||||
net.minecraft.core.IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
||||||
int b = bf.getInt(blockData);
|
int b = bf.getInt(blockData);
|
||||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
||||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
||||||
@@ -601,7 +615,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public java.awt.Color getBiomeColor(Location location, BiomeColor type) {
|
public Color getBiomeColor(Location location, BiomeColor type) {
|
||||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||||
var biome = holder.value();
|
var biome = holder.value();
|
||||||
@@ -734,6 +748,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::get)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a,b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
|
import net.minecraft.CrashReport;
|
||||||
|
import net.minecraft.CrashReportCategory;
|
||||||
|
import net.minecraft.ReportedException;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
@@ -26,38 +31,48 @@ import net.minecraft.util.random.WeightedList;
|
|||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.*;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.biome.*;
|
import net.minecraft.world.level.biome.*;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_21_R4.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_21_R4.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
import org.spigotmc.SpigotWorldConfig;
|
import org.spigotmc.SpigotWorldConfig;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Objects;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -174,8 +189,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var holder = registry.get(loc).orElse(null);
|
||||||
|
if (holder == null) return false;
|
||||||
|
var structure = holder.value();
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
holder,
|
||||||
|
levelKey,
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
biome -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -208,11 +276,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -220,7 +283,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -230,22 +293,70 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
for (int j = 0; j < list.size(); j++) {
|
||||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
Structure structure = list.get(j);
|
||||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||||
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
}
|
||||||
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinY() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxY();
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -263,9 +374,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinY(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,7 +418,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ package com.volmit.iris.core.nms.v1_21_R4;
|
|||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import com.volmit.iris.util.agent.Agent;
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -61,6 +65,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -93,6 +99,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
@@ -734,6 +741,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::get)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ import com.volmit.iris.engine.framework.ResultLocator;
|
|||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
|
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||||
|
import net.minecraft.CrashReport;
|
||||||
|
import net.minecraft.CrashReportCategory;
|
||||||
|
import net.minecraft.ReportedException;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
@@ -26,38 +31,48 @@ import net.minecraft.util.random.WeightedList;
|
|||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.*;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.biome.*;
|
import net.minecraft.world.level.biome.*;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
import net.minecraft.world.level.levelgen.*;
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_21_R5.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.v1_21_R5.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.generator.structure.CraftStructure;
|
||||||
|
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||||
import org.spigotmc.SpigotWorldConfig;
|
import org.spigotmc.SpigotWorldConfig;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Objects;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||||
private final ChunkGenerator delegate;
|
private final ChunkGenerator delegate;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
private final IrisStructurePopulator populator;
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
this.populator = new IrisStructurePopulator(engine);
|
||||||
var dimension = engine.getDimension();
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
@@ -174,8 +189,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
var chunkPos = access.getPos();
|
||||||
|
var sectionPos = SectionPos.bottomOf(access);
|
||||||
|
var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||||
|
var loc = ResourceLocation.tryParse(key);
|
||||||
|
if (loc == null) return false;
|
||||||
|
var holder = registry.get(loc).orElse(null);
|
||||||
|
if (holder == null) return false;
|
||||||
|
var structure = holder.value();
|
||||||
|
var biomes = structure.biomes();
|
||||||
|
|
||||||
|
var start = structure.generate(
|
||||||
|
holder,
|
||||||
|
levelKey,
|
||||||
|
registryAccess,
|
||||||
|
this,
|
||||||
|
biomeSource,
|
||||||
|
structureState.randomState(),
|
||||||
|
templateManager,
|
||||||
|
structureState.getLevelSeed(),
|
||||||
|
chunkPos,
|
||||||
|
fetchReferences(structureManager, access, sectionPos, structure),
|
||||||
|
access,
|
||||||
|
biome -> ignoreBiomes || biomes.contains(biome)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BoundingBox box = start.getBoundingBox();
|
||||||
|
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||||
|
structureManager.level.getMinecraftWorld().getWorld(),
|
||||||
|
CraftStructure.minecraftToBukkit(structure),
|
||||||
|
new org.bukkit.util.BoundingBox(
|
||||||
|
box.minX(),
|
||||||
|
box.minY(),
|
||||||
|
box.minZ(),
|
||||||
|
box.maxX(),
|
||||||
|
box.maxY(),
|
||||||
|
box.maxZ()
|
||||||
|
), chunkPos.x, chunkPos.z);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
if (!event.isCancelled()) {
|
||||||
|
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||||
|
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||||
|
return structurestart != null ? structurestart.getReferences() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -208,11 +276,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
@@ -220,7 +283,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -230,22 +293,70 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
if (!structureManager.shouldGenerateStructures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
|
||||||
|
BlockPos blockPos = sectionPos.origin();
|
||||||
|
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||||
|
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||||
|
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||||
|
var list = structures.stream()
|
||||||
|
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||||
|
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||||
|
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||||
|
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||||
|
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
int wX = x + blockPos.getX();
|
||||||
|
int wZ = z + blockPos.getZ();
|
||||||
|
|
||||||
|
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||||
|
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||||
|
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||||
|
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
for (int j = 0; j < list.size(); j++) {
|
||||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
Structure structure = list.get(j);
|
||||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||||
|
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
level.setCurrentlyGenerating(supplier);
|
||||||
|
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||||
|
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||||
|
category.setDetail("Description", supplier::get);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||||
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
}
|
||||||
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
|
||||||
|
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||||
|
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||||
|
int minX = chunkPos.getMinBlockX();
|
||||||
|
int minZ = chunkPos.getMinBlockZ();
|
||||||
|
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||||
|
int minY = heightAccessor.getMinY() + 1;
|
||||||
|
int maxY = heightAccessor.getMaxY();
|
||||||
|
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -263,9 +374,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
return delegate.getGenDepth();
|
return delegate.getGenDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
int block = engine.getHeight(i, j, true);
|
||||||
|
int water = engine.getHeight(i, j, false);
|
||||||
|
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||||
|
for (int k = 0; k < column.length; k++) {
|
||||||
|
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||||
|
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||||
|
else column[k] = Blocks.AIR.defaultBlockState();
|
||||||
|
}
|
||||||
|
return new NoiseColumn(levelheightaccessor.getMinY(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,7 +418,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
}
|
}
|
||||||
if (biomeSource == null)
|
if (biomeSource == null)
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
|
||||||
|
Method setHeight = null;
|
||||||
|
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||||
|
var types = method.getParameterTypes();
|
||||||
|
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||||
|
|| !method.getReturnType().equals(void.class))
|
||||||
|
continue;
|
||||||
|
setHeight = method;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (setHeight == null)
|
||||||
|
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||||
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ package com.volmit.iris.core.nms.v1_21_R5;
|
|||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.link.Identifier;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import com.volmit.iris.util.agent.Agent;
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -60,6 +64,8 @@ import net.minecraft.world.level.dimension.LevelStem;
|
|||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -92,6 +98,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
@@ -733,6 +740,71 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void placeStructures(Chunk chunk) {
|
||||||
|
var craft = ((CraftChunk) chunk);
|
||||||
|
var level = craft.getCraftWorld().getHandle();
|
||||||
|
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||||
|
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||||
|
var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET);
|
||||||
|
var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||||
|
return structureSets.keySet()
|
||||||
|
.stream()
|
||||||
|
.map(structureSets::get)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(holder -> {
|
||||||
|
var set = holder.value();
|
||||||
|
var placement = set.placement();
|
||||||
|
var key = holder.key().location();
|
||||||
|
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||||
|
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||||
|
builder = StructurePlacement.RandomSpread.builder()
|
||||||
|
.separation(random.separation())
|
||||||
|
.spacing(random.spacing())
|
||||||
|
.spreadType(switch (random.spreadType()) {
|
||||||
|
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||||
|
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||||
|
});
|
||||||
|
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||||
|
builder = StructurePlacement.ConcentricRings.builder()
|
||||||
|
.distance(rings.distance())
|
||||||
|
.spread(rings.spread())
|
||||||
|
.count(rings.count());
|
||||||
|
} else {
|
||||||
|
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
|
||||||
|
.salt(placement.salt)
|
||||||
|
.frequency(placement.frequency)
|
||||||
|
.structures(set.structures()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new StructurePlacement.Structure(
|
||||||
|
entry.weight(),
|
||||||
|
entry.structure()
|
||||||
|
.unwrapKey()
|
||||||
|
.map(ResourceKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.orElse(null),
|
||||||
|
entry.structure().tags()
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.toList()
|
||||||
|
))
|
||||||
|
.filter(StructurePlacement.Structure::isValid)
|
||||||
|
.toList())
|
||||||
|
.build());
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a, KMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|||||||
Reference in New Issue
Block a user