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.lang)
|
||||
slim(libs.commons.lang3)
|
||||
slim(libs.commons.math3)
|
||||
slim(libs.oshi)
|
||||
slim(libs.lz4)
|
||||
slim(libs.fastutil)
|
||||
|
||||
@@ -18,19 +18,29 @@
|
||||
|
||||
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.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.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
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.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||
import com.volmit.iris.util.decree.annotations.Decree;
|
||||
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.Form;
|
||||
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.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import lombok.SneakyThrows;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
||||
@@ -59,6 +70,8 @@ import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
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.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")
|
||||
public void packBenchmark(
|
||||
@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 snippedBase = "snippet/" + snippetType + "/";
|
||||
|
||||
return new TypeAdapter<>() {
|
||||
@Override
|
||||
@@ -438,19 +439,20 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
|
||||
if (reader.peek().equals(JsonToken.STRING)) {
|
||||
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");
|
||||
|
||||
if (f.exists()) {
|
||||
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
|
||||
return adapter.read(snippetReader);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Couldn't read snippet " + r + " in " + reader.getPath() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
} else {
|
||||
Iris.error("Couldn't find snippet " + r + " in " + reader.getPath());
|
||||
File f = new File(getDataFolder(), r + ".json");
|
||||
if (f.exists()) {
|
||||
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
|
||||
return adapter.read(snippetReader);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Couldn't read snippet " + r + " in " + reader.getPath() + " (" + e.getMessage() + ")");
|
||||
}
|
||||
} else {
|
||||
Iris.error("Couldn't find snippet " + r + " in " + reader.getPath());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -488,7 +490,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
.map(s -> s.substring(absPath.length() + 1))
|
||||
.map(s -> s.replace("\\", "/"))
|
||||
.map(s -> s.split("\\Q.\\E")[0])
|
||||
.forEach(s -> l.add("snippet/" + f + "/" + s));
|
||||
.forEach(s -> l.add("snippet/" + s));
|
||||
} catch (Throwable e) {
|
||||
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.plugin.VolmitSender;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
@@ -39,6 +40,7 @@ public abstract class IrisRegistrant {
|
||||
@ArrayType(min = 1, type = String.class)
|
||||
private KList<String> preprocessors = new KList<>();
|
||||
|
||||
@EqualsAndHashCode.Exclude
|
||||
private transient IrisData loader;
|
||||
|
||||
private transient String loadKey;
|
||||
|
||||
@@ -18,8 +18,12 @@
|
||||
|
||||
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.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.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||
@@ -136,4 +140,8 @@ public interface INMSBinding {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||
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.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
@@ -135,6 +138,16 @@ public class NMSBinding1X implements INMSBinding {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeStructures(Chunk chunk) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMap<Identifier, StructurePlacement> collectStructures() {
|
||||
return new KMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeEntity(Entity location) {
|
||||
return null;
|
||||
|
||||
@@ -167,6 +167,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
||||
|
||||
@BlockCoordinates
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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.object.annotations.*;
|
||||
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.json.JSONObject;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
@@ -40,6 +41,11 @@ import lombok.experimental.Accessors;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
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)
|
||||
@Required
|
||||
@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")
|
||||
private boolean disableInitialRotation = false;
|
||||
|
||||
@RegistryListFunction(StructureKeyFunction.class)
|
||||
@RegistryListFunction(StructureKeyOrTagFunction.class)
|
||||
@Desc("The minecraft key to use when creating treasure maps")
|
||||
private String structureKey = null;
|
||||
|
||||
@@ -117,6 +123,10 @@ public class IrisJigsawStructure extends IrisRegistrant {
|
||||
|
||||
public int getMaxDimension() {
|
||||
return maxDimension.aquire(() -> {
|
||||
if (datapackStructures.isNotEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (useMaxPieceSizeForParallaxRadius) {
|
||||
int max = 0;
|
||||
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
|
||||
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));
|
||||
}
|
||||
futures.add(CompletableFuture.runAsync(() -> INMS.get().placeStructures(c), syncExecutor));
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.thenRunAsync(() -> {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
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 final ConcurrentHashMap<T, Boolean> map;
|
||||
|
||||
public KSet() {
|
||||
map = new ConcurrentHashMap<>();
|
||||
public KSet(Collection<? extends T> c) {
|
||||
this(c.size());
|
||||
addAll(c);
|
||||
}
|
||||
|
||||
public KSet(Collection<? extends T> c) {
|
||||
this();
|
||||
addAll(c);
|
||||
@SafeVarargs
|
||||
public KSet(T... values) {
|
||||
this(values.length);
|
||||
addAll(Arrays.asList(values));
|
||||
}
|
||||
|
||||
public KSet(int initialCapacity, float loadFactor) {
|
||||
@@ -47,6 +50,13 @@ public class KSet<T> extends AbstractSet<T> implements Serializable {
|
||||
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
|
||||
public int 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-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-math3 = "3.6.1"
|
||||
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
|
||||
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-lang = { module = "commons-lang:commons-lang", version.ref = "commons-lang" }
|
||||
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" }
|
||||
lz4 = { module = "org.lz4:lz4-java", version.ref = "lz4" }
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -27,45 +29,55 @@ import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -182,8 +194,58 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||
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
|
||||
@@ -201,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -213,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -236,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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 {
|
||||
@@ -251,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
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.datafixers.util.Pair;
|
||||
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.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
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.util.agent.Agent;
|
||||
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.storage.LevelStorageSource;
|
||||
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.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@@ -79,7 +84,6 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -90,6 +94,7 @@ import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -27,45 +29,55 @@ import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -182,8 +194,58 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||
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
|
||||
@@ -201,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -213,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -236,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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 {
|
||||
@@ -251,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
package com.volmit.iris.core.nms.v1_20_R2;
|
||||
|
||||
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.volmit.iris.Iris;
|
||||
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.BlockProperty;
|
||||
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.palette.*;
|
||||
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 it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -27,45 +29,55 @@ import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -182,8 +194,58 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||
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
|
||||
@@ -201,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -213,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -236,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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 {
|
||||
@@ -251,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
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.datafixers.util.Pair;
|
||||
import com.volmit.iris.Iris;
|
||||
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.BlockProperty;
|
||||
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.palette.*;
|
||||
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 it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -27,43 +29,53 @@ import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Objects;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -180,8 +192,59 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||
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
|
||||
@@ -199,11 +262,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -211,7 +269,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -234,9 +360,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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 {
|
||||
@@ -249,7 +388,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
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.datafixers.util.Pair;
|
||||
import com.volmit.iris.Iris;
|
||||
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.BlockProperty;
|
||||
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
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.nbt.mca.NBTWorld;
|
||||
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.scheduling.J;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -27,44 +29,54 @@ import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -181,8 +193,59 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||
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
|
||||
@@ -200,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -212,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -235,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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 {
|
||||
@@ -250,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
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.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.core.nms.INMSBinding;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
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.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -27,43 +29,54 @@ import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.NoiseColumn;
|
||||
import net.minecraft.world.level.StructureManager;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
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.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -180,8 +193,59 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager) {
|
||||
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
|
||||
@@ -199,11 +263,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -211,7 +270,75 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -234,9 +361,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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 {
|
||||
@@ -249,7 +389,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
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.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
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.datapack.DataVersion;
|
||||
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.palette.*;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
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.registries.Registries;
|
||||
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.level.*;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -174,8 +190,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||
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
|
||||
@@ -208,11 +277,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -220,7 +284,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,22 +294,70 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -263,9 +375,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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
|
||||
@@ -294,7 +419,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
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.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
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.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
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.util.agent.Agent;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
import org.bukkit.*;
|
||||
@@ -89,10 +97,16 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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 {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
@@ -179,14 +193,14 @@ public class NMSBinding implements INMSBinding {
|
||||
}
|
||||
|
||||
@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;
|
||||
return switch (tag) {
|
||||
case CollectionTag<?> collection -> {
|
||||
KList<Object> list = new KList<>();
|
||||
|
||||
for (Object i : collection) {
|
||||
if (i instanceof net.minecraft.nbt.Tag t)
|
||||
if (i instanceof Tag t)
|
||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
||||
else list.add(i);
|
||||
}
|
||||
@@ -243,7 +257,7 @@ public class NMSBinding implements INMSBinding {
|
||||
yield tag;
|
||||
}
|
||||
case List<?> list -> {
|
||||
var tag = new net.minecraft.nbt.ListTag();
|
||||
var tag = new ListTag();
|
||||
for (var i : list) {
|
||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
||||
}
|
||||
@@ -497,13 +511,13 @@ public class NMSBinding implements INMSBinding {
|
||||
@Override
|
||||
public MCAPaletteAccess createPalette() {
|
||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
||||
Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
||||
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
||||
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
||||
Field cf = IdMapper.class.getDeclaredField("tToId");
|
||||
Field df = IdMapper.class.getDeclaredField("idToT");
|
||||
Field bf = IdMapper.class.getDeclaredField("nextId");
|
||||
cf.setAccessible(true);
|
||||
df.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);
|
||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
||||
@@ -601,7 +615,7 @@ public class NMSBinding implements INMSBinding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.awt.Color getBiomeColor(Location location, BiomeColor type) {
|
||||
public Color getBiomeColor(Location location, BiomeColor type) {
|
||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
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.registries.Registries;
|
||||
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.level.*;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -174,8 +189,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||
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
|
||||
@@ -208,11 +276,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -220,7 +283,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,22 +293,70 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -263,9 +374,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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
|
||||
@@ -294,7 +418,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
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.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
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.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
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.util.agent.Agent;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
import org.bukkit.*;
|
||||
@@ -93,6 +99,7 @@ import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
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.object.IrisJigsawStructure;
|
||||
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.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
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.registries.Registries;
|
||||
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.level.*;
|
||||
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.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
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.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
@@ -174,8 +189,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||
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
|
||||
@@ -208,11 +276,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
@@ -220,7 +283,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,22 +293,70 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
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
|
||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -263,9 +374,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
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
|
||||
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
|
||||
@@ -294,7 +418,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
}
|
||||
if (biomeSource == null)
|
||||
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());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
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.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
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.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
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.util.agent.Agent;
|
||||
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.flat.FlatLayerInfo;
|
||||
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.PrimaryLevelData;
|
||||
import org.bukkit.*;
|
||||
@@ -92,6 +98,7 @@ import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||
|
||||
Reference in New Issue
Block a user