im not getting Jiggy with it

This commit is contained in:
Brian Neumann-Fopiano
2026-02-19 02:02:31 -05:00
parent d643461e2e
commit b491d9efd0
49 changed files with 168 additions and 2872 deletions

View File

@@ -224,6 +224,8 @@ public class IrisSettings {
public boolean useConsoleCustomColors = true;
public boolean useCustomColorsIngame = true;
public boolean adjustVanillaHeight = false;
public boolean importExternalDatapacks = true;
public boolean autoGenerateIntrinsicStructures = true;
public String forceMainWorld = "";
public int spinh = -20;
public int spins = 7;

View File

@@ -27,6 +27,7 @@ import art.arcane.iris.engine.object.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.volmlib.util.io.IO;
import art.arcane.iris.util.common.format.C;
import art.arcane.iris.util.common.misc.ServerProperties;
import art.arcane.iris.util.common.plugin.VolmitSender;
@@ -41,6 +42,8 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -119,6 +122,7 @@ public class ServerConfigurator {
Iris.info("Checking Data Packs...");
DimensionHeight height = new DimensionHeight(fixer);
KList<File> folders = getDatapacksFolder();
installExternalDataPacks(folders);
KMap<String, KSet<String>> biomes = new KMap<>();
try (Stream<IrisData> stream = allPacks()) {
@@ -136,6 +140,52 @@ public class ServerConfigurator {
return fullInstall && verifyDataPacksPost(IrisSettings.get().getAutoConfiguration().isAutoRestartOnCustomBiomeInstall());
}
private static void installExternalDataPacks(KList<File> folders) {
if (!IrisSettings.get().getGeneral().isImportExternalDatapacks()) {
return;
}
File source = Iris.instance.getDataFolder("datapacks");
source.mkdirs();
File[] datapacks = source.listFiles();
if (datapacks == null || datapacks.length == 0) {
return;
}
int copied = 0;
for (File targetFolder : folders) {
targetFolder.mkdirs();
for (File entry : datapacks) {
if (entry == null || !entry.exists() || entry.getName().startsWith(".")) {
continue;
}
File output = new File(targetFolder, entry.getName());
try {
if (entry.isDirectory()) {
IO.copyDirectory(entry.toPath(), output.toPath());
} else if (entry.isFile()) {
File parent = output.getParentFile();
if (parent != null) {
parent.mkdirs();
}
Files.copy(entry.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
} else {
continue;
}
copied++;
} catch (Throwable e) {
Iris.warn("Failed to install datapack \"" + entry.getName() + "\" into \"" + targetFolder.getPath() + "\"");
Iris.reportError(e);
}
}
}
if (copied > 0) {
Iris.info("Installed " + copied + " external datapack copy operation" + (copied == 1 ? "" : "s") + " from " + source.getPath());
}
}
private static boolean verifyDataPacksPost(boolean allowRestarting) {
try (Stream<IrisData> stream = allPacks()) {
boolean bad = stream

View File

@@ -34,13 +34,10 @@ import art.arcane.iris.core.tools.IrisToolbelt;
import art.arcane.iris.engine.IrisEngineMantle;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisPosition;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.iris.engine.object.annotations.Snippet;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.iris.util.project.context.IrisContext;
import art.arcane.iris.engine.object.IrisJigsawStructurePlacement;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.volmlib.util.director.DirectorOrigin;
@@ -199,14 +196,10 @@ public class CommandDeveloper implements DirectorExecutor {
IrisData dm = IrisData.get(Iris.instance.getDataFolder("packs", type.getLoadKey()));
var loader = dm.getObjectLoader();
var processed = new KMap<String, IrisPosition>();
var objects = loader.getPossibleKeys();
var pieces = dm.getJigsawPieceLoader().getPossibleKeys();
var sender = sender();
sender.sendMessage(C.IRIS + "Found " + objects.length + " objects in " + type.getLoadKey());
sender.sendMessage(C.IRIS + "Found " + pieces.length + " jigsaw pieces in " + type.getLoadKey());
final int total = objects.length;
final AtomicInteger completed = new AtomicInteger();
@@ -220,45 +213,7 @@ public class CommandDeveloper implements DirectorExecutor {
@Override
public void execute() {
Arrays.stream(pieces).parallel()
.map(dm.getJigsawPieceLoader()::load)
.filter(Objects::nonNull)
.forEach(piece -> {
var offset = processed.compute(piece.getObject(), (key, o) -> {
if (o != null) return o;
var obj = loader.load(key);
if (obj == null) return new IrisPosition();
obj.shrinkwrap();
try {
if (!obj.getShrinkOffset().isZero()) {
changed.incrementAndGet();
obj.write(obj.getLoadFile());
}
completeWork();
} catch (IOException e) {
Iris.error("Failed to write object " + obj.getLoadKey());
e.printStackTrace();
return new IrisPosition();
}
return new IrisPosition(obj.getShrinkOffset());
});
if (offset.getX() == 0 && offset.getY() == 0 && offset.getZ() == 0)
return;
piece.getConnectors().forEach(connector -> connector.setPosition(connector.getPosition().add(offset)));
try {
IO.writeAll(piece.getLoadFile(), dm.getGson().toJson(piece));
} catch (IOException e) {
Iris.error("Failed to write jigsaw piece " + piece.getLoadKey());
e.printStackTrace();
}
});
Arrays.stream(loader.getPossibleKeys()).parallel()
.filter(key -> !processed.containsKey(key))
.map(loader::load)
.forEach(obj -> {
if (obj == null) {
@@ -368,7 +323,6 @@ public class CommandDeveloper implements DirectorExecutor {
}
}
@SneakyThrows
@Director(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 = "null", customHandler = NullableDimensionHandler.class)
@@ -376,120 +330,8 @@ public class CommandDeveloper implements DirectorExecutor {
@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();
sender().sendMessage(C.YELLOW + "Legacy structure conversion hooks have been removed.");
sender().sendMessage(C.YELLOW + "Use intrinsic structure generation and datapack ingestion instead.");
}
@Director(description = "Test")

View File

@@ -132,58 +132,4 @@ public class CommandEdit implements DirectorExecutor {
}
}
@Director(description = "Edit the structure file you specified", aliases = {"jigsawstructure", "structure"}, origin = DirectorOrigin.PLAYER)
public void jigsaw(@Param(contextual = false, description = "The jigsaw structure to edit") IrisJigsawStructure jigsaw) {
if (noStudio()) {
return;
}
try {
if (jigsaw == null || jigsaw.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
return;
}
Desktop.getDesktop().open(jigsaw.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + jigsaw.getTypeName() + " " + jigsaw.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
}
}
@Director(description = "Edit the pool file you specified", aliases = {"jigsawpool", "pool"}, origin = DirectorOrigin.PLAYER)
public void jigsawPool(@Param(contextual = false, description = "The jigsaw pool to edit") IrisJigsawPool pool) {
if (noStudio()) {
return;
}
try {
if (pool == null || pool.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
return;
}
Desktop.getDesktop().open(pool.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + pool.getTypeName() + " " + pool.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
}
}
@Director(description = "Edit the jigsaw piece file you specified", aliases = {"jigsawpiece", "piece"}, origin = DirectorOrigin.PLAYER)
public void jigsawPiece(@Param(contextual = false, description = "The jigsaw piece to edit") IrisJigsawPiece piece) {
if (noStudio()) {
return;
}
try {
if (piece == null || piece.getLoadFile() == null) {
sender().sendMessage(C.GOLD + "Cannot find the file; Perhaps it was not loaded directly from a file?");
return;
}
Desktop.getDesktop().open(piece.getLoadFile());
sender().sendMessage(C.GREEN + "Opening " + piece.getTypeName() + " " + piece.getLoadFile().getName().split("\\Q.\\E")[0] + " in VSCode! ");
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cant find the file. Or registrant does not exist");
}
}
}

View File

@@ -20,7 +20,6 @@ package art.arcane.iris.core.commands;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisJigsawStructure;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.volmlib.util.director.DirectorOrigin;
@@ -65,23 +64,6 @@ public class CommandFind implements DirectorExecutor {
e.gotoRegion(region, player(), teleport);
}
@Director(description = "Find a structure")
public void structure(
@Param(description = "The structure to look for")
IrisJigsawStructure structure,
@Param(description = "Should you be teleported", defaultValue = "true")
boolean teleport
) {
Engine e = engine();
if (e == null) {
sender().sendMessage(C.GOLD + "Not in an Iris World!");
return;
}
e.gotoJigsaw(structure, player(), teleport);
}
@Director(description = "Find a point of interest.")
public void poi(
@Param(description = "The type of PoI to look for.")

View File

@@ -109,7 +109,6 @@ public class CommandIris implements DirectorExecutor {
private CommandPregen pregen;
private CommandSettings settings;
private CommandObject object;
private CommandJigsaw jigsaw;
private CommandWhat what;
private CommandEdit edit;
private CommandFind find;
@@ -694,7 +693,7 @@ public class CommandIris implements DirectorExecutor {
+ C.GOLD + threadCount + C.GREEN + " worker(s). "
+ C.GRAY + "Progress is shown on-screen.");
if (regenMode == RegenMode.TERRAIN) {
Iris.warn("Regen running in terrain mode; mantle object/jigsaw stages are bypassed. Use mode=full to regenerate objects.");
Iris.warn("Regen running in terrain mode; mantle object stages are bypassed. Use mode=full to regenerate objects.");
}
Iris.info("Regen run start: id=" + runId
@@ -1497,7 +1496,6 @@ public class CommandIris implements DirectorExecutor {
return new RegenMantleChunkState(
chunk.isFlagged(MantleFlag.PLANNED),
chunk.isFlagged(MantleFlag.OBJECT),
chunk.isFlagged(MantleFlag.JIGSAW),
chunk.isFlagged(MantleFlag.REAL),
blockDataEntries.get(),
stringEntries.get(),
@@ -1855,7 +1853,6 @@ public class CommandIris implements DirectorExecutor {
private record RegenMantleChunkState(
boolean planned,
boolean objectFlag,
boolean jigsawFlag,
boolean realFlag,
int blockDataEntries,
int stringEntries,
@@ -1865,7 +1862,6 @@ public class CommandIris implements DirectorExecutor {
private String describe() {
return "flags[planned=" + planned
+ ",object=" + objectFlag
+ ",jigsaw=" + jigsawFlag
+ ",real=" + realFlag
+ "] slices[blockData=" + blockDataEntries
+ ",strings=" + stringEntries

View File

@@ -1,121 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.core.commands;
import art.arcane.iris.Iris;
import art.arcane.iris.core.edit.JigsawEditor;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.framework.placer.WorldObjectPlacer;
import art.arcane.iris.engine.jigsaw.PlannedStructure;
import art.arcane.iris.engine.object.IrisJigsawPiece;
import art.arcane.iris.engine.object.IrisJigsawStructure;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.engine.object.IrisPosition;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.volmlib.util.director.DirectorOrigin;
import art.arcane.volmlib.util.director.annotations.Director;
import art.arcane.volmlib.util.director.annotations.Param;
import art.arcane.iris.util.common.director.specialhandlers.ObjectHandler;
import art.arcane.iris.util.common.format.C;
import art.arcane.volmlib.util.format.Form;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import java.io.File;
@Director(name = "jigsaw", origin = DirectorOrigin.PLAYER, studio = true, description = "Iris jigsaw commands")
public class CommandJigsaw implements DirectorExecutor {
@Director(description = "Edit a jigsaw piece")
public void edit(
@Param(description = "The jigsaw piece to edit")
IrisJigsawPiece piece
) {
File dest = piece.getLoadFile();
new JigsawEditor(player(), piece, IrisData.loadAnyObject(piece.getObject(), data()), dest);
}
@Director(description = "Place a jigsaw structure")
public void place(
@Param(description = "The jigsaw structure to place")
IrisJigsawStructure structure
) {
PrecisionStopwatch p = PrecisionStopwatch.start();
try {
var world = world();
WorldObjectPlacer placer = new WorldObjectPlacer(world);
PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation().add(0, world.getMinHeight(), 0)), new RNG(), true);
VolmitSender sender = sender();
sender.sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2));
ps.place(placer, failed -> sender.sendMessage(failed ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place the structure!"));
} catch (IllegalArgumentException e) {
sender().sendMessage(C.RED + "Failed to place the structure: " + e.getMessage());
}
}
@Director(description = "Create a jigsaw piece")
public void create(
@Param(description = "The name of the jigsaw piece")
String piece,
@Param(description = "The project to add the jigsaw piece to")
String project,
@Param(description = "The object to use for this piece", customHandler = ObjectHandler.class)
String object
) {
IrisObject o = IrisData.loadAnyObject(object, data());
if (object == null) {
sender().sendMessage(C.RED + "Failed to find existing object");
return;
}
File dest = Iris.instance.getDataFile("packs", project, "jigsaw-pieces", piece + ".json");
new JigsawEditor(player(), null, o, dest);
sender().sendMessage(C.GRAY + "* Right Click blocks to make them connectors");
sender().sendMessage(C.GRAY + "* Right Click connectors to orient them");
sender().sendMessage(C.GRAY + "* Shift + Right Click connectors to remove them");
sender().sendMessage(C.GREEN + "Remember to use /iris jigsaw save");
}
@Director(description = "Exit the current jigsaw editor")
public void exit() {
JigsawEditor editor = JigsawEditor.editors.get(player());
if (editor == null) {
sender().sendMessage(C.GOLD + "You don't have any pieces open to exit!");
return;
}
editor.exit();
sender().sendMessage(C.GREEN + "Exited Jigsaw Editor");
}
@Director(description = "Save & Exit the current jigsaw editor")
public void save() {
JigsawEditor editor = JigsawEditor.editors.get(player());
if (editor == null) {
sender().sendMessage(C.GOLD + "You don't have any pieces open to save!");
return;
}
editor.close();
sender().sendMessage(C.GREEN + "Saved & Exited Jigsaw Editor");
}
}

View File

@@ -416,67 +416,7 @@ public class CommandStudio implements DirectorExecutor {
@Director(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DirectorOrigin.PLAYER)
public void distances(@Param(description = "The radius in chunks") int radius) {
var engine = engine();
if (engine == null) {
sender().sendMessage(C.RED + "Only works in an Iris world!");
return;
}
var sender = sender();
int d = radius * 2;
KMap<String, KList<Position2>> data = new KMap<>();
var multiBurst = new MultiBurst("Distance Sampler");
var executor = multiBurst.burst(radius * radius);
sender.sendMessage(C.GRAY + "Generating data...");
var loc = player().getLocation();
int totalTasks = d * d;
AtomicInteger completedTasks = new AtomicInteger(0);
int c = J.ar(() -> {
sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding structures");
}, 0);
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
var struct = engine.getStructureAt(x, z);
if (struct != null) {
data.computeIfAbsent(struct.getLoadKey(), (k) -> new KList<>()).add(new Position2(x, z));
}
completedTasks.incrementAndGet();
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
executor.complete();
multiBurst.close();
J.car(c);
for (var key : data.keySet()) {
var list = data.get(key);
KList<Long> distances = new KList<>(list.size() - 1);
for (int i = 0; i < list.size(); i++) {
var pos = list.get(i);
double dist = Integer.MAX_VALUE;
for (var p : list) {
if (p.equals(pos)) continue;
dist = Math.min(dist, Math.sqrt(Math.pow(pos.getX() - p.getX(), 2) + Math.pow(pos.getZ() - p.getZ(), 2)));
}
if (dist == Integer.MAX_VALUE) continue;
distances.add(Math.round(dist * 16));
}
long[] array = new long[distances.size()];
for (int i = 0; i < distances.size(); i++) {
array[i] = distances.get(i);
}
Arrays.sort(array);
long min = array.length > 0 ? array[0] : 0;
long max = array.length > 0 ? array[array.length - 1] : 0;
long sum = Arrays.stream(array).sum();
long avg = array.length > 0 ? Math.round(sum / (double) array.length) : 0;
String msg = "%s: %s => min: %s/max: %s -> avg: %s".formatted(key, list.size(), min, max, avg);
sender.sendMessage(msg);
}
if (data.isEmpty()) {
sender.sendMessage(C.RED + "No data found!");
} else {
sender.sendMessage(C.GREEN + "Done!");
}
sender().sendMessage(C.YELLOW + "Structure distance sampling for legacy structure data has been removed.");
}

View File

@@ -1,253 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.core.edit;
import com.google.gson.Gson;
import art.arcane.iris.Iris;
import art.arcane.iris.core.service.WandSVC;
import art.arcane.iris.engine.object.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.data.Cuboid;
import art.arcane.volmlib.util.io.IO;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.scheduling.ChronoLatch;
import art.arcane.iris.util.common.scheduling.J;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class JigsawEditor implements Listener {
public static final KMap<Player, JigsawEditor> editors = new KMap<>();
private final Player player;
private final IrisObject object;
private final File targetSaveLocation;
private final IrisJigsawPiece piece;
private final Location origin;
private final Cuboid cuboid;
private final int ticker;
private final KMap<IrisPosition, Runnable> falling = new KMap<>();
private final ChronoLatch cl = new ChronoLatch(100);
private Location target;
public JigsawEditor(Player player, IrisJigsawPiece piece, IrisObject object, File saveLocation) {
if (object == null) throw new RuntimeException("Object is null! " + piece.getObject());
editors.compute(player, ($, current) -> {
if (current != null) {
current.exit();
}
return this;
});
this.object = object;
this.player = player;
origin = player.getLocation().clone().add(0, 7, 0);
target = origin;
this.targetSaveLocation = saveLocation;
this.piece = piece == null ? new IrisJigsawPiece() : piece;
this.piece.setObject(object.getLoadKey());
cuboid = new Cuboid(origin.clone(), origin.clone().add(object.getW() - 1, object.getH() - 1, object.getD() - 1));
ticker = J.sr(this::onTick, 0);
J.s(() -> object.placeCenterY(origin));
Iris.instance.registerListener(this);
}
@EventHandler
public void on(PlayerMoveEvent e) {
if (e.getPlayer().equals(player)) {
try {
target = player.getTargetBlockExact(7).getLocation();
} catch (Throwable ex) {
Iris.reportError(ex);
target = player.getLocation();
return;
}
if (cuboid.contains(target)) {
for (IrisPosition i : falling.k()) {
Location at = toLocation(i);
if (at.equals(target)) {
falling.remove(i).run();
}
}
}
}
}
public Location toLocation(IrisPosition i) {
return origin.clone()
.add(new Vector(i.getX(), i.getY(), i.getZ()))
.add(object.getCenter())
.getBlock()
.getLocation();
}
public IrisPosition toPosition(Location l) {
return new IrisPosition(l.clone().getBlock().getLocation()
.subtract(origin.clone())
.subtract(object.getCenter())
.add(1, 1, 1)
.toVector());
}
@EventHandler
public void on(PlayerInteractEvent e) {
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
if (e.getClickedBlock() != null && cuboid.contains(e.getClickedBlock().getLocation()) && e.getPlayer().equals(player)) {
IrisPosition pos = toPosition(e.getClickedBlock().getLocation());
IrisJigsawPieceConnector connector = null;
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
if (i.getPosition().equals(pos)) {
connector = i;
break;
}
}
if (!player.isSneaking() && connector == null) {
connector = new IrisJigsawPieceConnector();
connector.setDirection(IrisDirection.getDirection(e.getBlockFace()));
connector.setPosition(pos);
piece.getConnectors().add(connector);
player.playSound(e.getClickedBlock().getLocation(), Sound.ENTITY_ITEM_FRAME_ADD_ITEM, 1f, 1f);
} else if (player.isSneaking() && connector != null) {
piece.getConnectors().remove(connector);
player.playSound(e.getClickedBlock().getLocation(), Sound.ENTITY_ITEM_FRAME_REMOVE_ITEM, 1f, 1f);
} else if (connector != null && !player.isSneaking()) {
connector.setDirection(IrisDirection.getDirection(e.getBlockFace()));
player.playSound(e.getClickedBlock().getLocation(), Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 1f, 1f);
}
}
}
}
private void removeKey(JSONObject o, String... path) {
if (path.length == 1) {
o.remove(path[0]);
return;
}
List<String> s = new java.util.ArrayList<>(List.of(path));
s.remove(0);
removeKey(o.getJSONObject(path[0]), s.toArray(new String[0]));
}
private List<JSONObject> getObjectsInArray(JSONObject a) { // This gets all the objects in an array that are connectors
KList<JSONObject> o = new KList<>();
for (int i = 0; i < a.getJSONArray("connectors").length(); i++) {
o.add(a.getJSONArray("connectors").getJSONObject(i));
}
return o;
}
public void close() {
exit();
try {
JSONObject j = new JSONObject(new Gson().toJson(piece));
// Remove sub-key
removeKey(j, "placementOptions", "translateCenter"); // should work
J.attempt(() -> j.getJSONObject("placementOptions").remove("translateCenter")); // otherwise
// remove root key
removeKey(j, "placementOptions"); // should work
j.remove("placementOptions"); // otherwise
// Remove key in all objects in array
for (JSONObject i : getObjectsInArray(j)) {
removeKey(i, "rotateConnector");
}
IO.writeAll(targetSaveLocation, j.toString(4));
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
}
public void exit() {
J.car(ticker);
Iris.instance.unregisterListener(this);
try {
J.sfut(() -> {
object.unplaceCenterY(origin);
falling.v().forEach(Runnable::run);
}).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
editors.remove(player);
}
public void onTick() {
if (cl.flip()) {
Iris.service(WandSVC.class).draw(cuboid, player);
f:
for (IrisPosition i : falling.k()) {
for (IrisJigsawPieceConnector j : piece.getConnectors()) {
if (j.getPosition().equals(i)) {
continue f;
}
}
falling.remove(i).run();
}
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
IrisPosition pos = i.getPosition();
Location at = toLocation(pos);
Vector dir = i.getDirection().toVector().clone();
for (int ix = 0; ix < RNG.r.i(1, 3); ix++) {
at.getWorld().spawnParticle(Particle.SOUL_FIRE_FLAME, at.clone().getBlock().getLocation().add(0.25, 0.25, 0.25).add(RNG.r.d(0.5), RNG.r.d(0.5), RNG.r.d(0.5)), 0, dir.getX(), dir.getY(), dir.getZ(), 0.092 + RNG.r.d(-0.03, 0.08));
}
if (at.getBlock().getLocation().equals(target)) {
continue;
}
if (!falling.containsKey(pos)) {
if (at.getBlock().getType().isAir()) {
at.getBlock().setType(Material.STONE);
}
falling.put(pos, BlockSignal.forever(at.getBlock()));
}
}
}
}
}

View File

@@ -64,9 +64,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
private ResourceLoader<IrisRegion> regionLoader;
private ResourceLoader<IrisDimension> dimensionLoader;
private ResourceLoader<IrisGenerator> generatorLoader;
private ResourceLoader<IrisJigsawPiece> jigsawPieceLoader;
private ResourceLoader<IrisJigsawPool> jigsawPoolLoader;
private ResourceLoader<IrisJigsawStructure> jigsawStructureLoader;
private ResourceLoader<IrisEntity> entityLoader;
private ResourceLoader<IrisMarker> markerLoader;
private ResourceLoader<IrisSpawner> spawnerLoader;
@@ -141,14 +138,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
return loadAny(IrisMod.class, key, nearest);
}
public static IrisJigsawPiece loadAnyJigsawPiece(String key, @Nullable IrisData nearest) {
return loadAny(IrisJigsawPiece.class, key, nearest);
}
public static IrisJigsawPool loadAnyJigsawPool(String key, @Nullable IrisData nearest) {
return loadAny(IrisJigsawPool.class, key, nearest);
}
public static IrisEntity loadAnyEntity(String key, @Nullable IrisData nearest) {
return loadAny(IrisEntity.class, key, nearest);
}
@@ -193,10 +182,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
return loadAny(IrisDimension.class, key, nearest);
}
public static IrisJigsawStructure loadAnyJigsawStructure(String key, @Nullable IrisData nearest) {
return loadAny(IrisJigsawStructure.class, key, nearest);
}
public static IrisGenerator loadAnyGenerator(String key, @Nullable IrisData nearest) {
return loadAny(IrisGenerator.class, key, nearest);
}
@@ -366,9 +351,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.biomeLoader = registerLoader(IrisBiome.class);
this.modLoader = registerLoader(IrisMod.class);
this.dimensionLoader = registerLoader(IrisDimension.class);
this.jigsawPoolLoader = registerLoader(IrisJigsawPool.class);
this.jigsawStructureLoader = registerLoader(IrisJigsawStructure.class);
this.jigsawPieceLoader = registerLoader(IrisJigsawPiece.class);
this.generatorLoader = registerLoader(IrisGenerator.class);
this.caveLoader = registerLoader(IrisCave.class);
this.markerLoader = registerLoader(IrisMarker.class);

View File

@@ -1,7 +1,6 @@
package art.arcane.iris.core.nms.container;
import com.google.gson.JsonObject;
import art.arcane.iris.engine.object.IrisJigsawStructurePlacement.SpreadType;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
@@ -31,6 +30,11 @@ public abstract class StructurePlacement {
return (int) Math.round(Math.sqrt((double) frac.getDenominator() / frac.getNumerator()));
}
public enum SpreadType {
LINEAR,
TRIANGULAR
}
@Getter
@Accessors(chain = true, fluent = true)
@EqualsAndHashCode(callSuper = true)

View File

@@ -18,32 +18,19 @@
package art.arcane.iris.core.service;
import com.google.gson.Gson;
import art.arcane.iris.Iris;
import art.arcane.iris.engine.object.*;
import art.arcane.iris.core.nms.INMS;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.format.Form;
import art.arcane.volmlib.util.function.Consumer2;
import art.arcane.volmlib.util.io.Converter;
import art.arcane.volmlib.util.io.IO;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.volmlib.util.nbt.io.NBTUtil;
import art.arcane.volmlib.util.nbt.io.NamedTag;
import art.arcane.iris.util.nbt.common.mca.NBTWorld;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.volmlib.util.nbt.tag.IntTag;
import art.arcane.volmlib.util.nbt.tag.ListTag;
import art.arcane.iris.util.common.plugin.IrisService;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.iris.util.common.scheduling.J;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Jigsaw;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class ConversionSVC implements IrisService {
private KList<Converter> converters;
@@ -66,156 +53,91 @@ public class ConversionSVC implements IrisService {
}
private String toPoolName(String poolReference) {
return poolReference.split("\\Q:\\E")[1];
}
public void convertStructures(File in, File out, VolmitSender s) {
KMap<String, IrisJigsawPool> pools = new KMap<>();
KList<File> roots = new KList<>();
AtomicInteger total = new AtomicInteger(0);
AtomicInteger at = new AtomicInteger(0);
File destPools = new File(out.getAbsolutePath() + "/jigsaw-pools");
destPools.mkdirs();
findAllNBT(in, (folder, file) -> {
total.getAndIncrement();
if (roots.addIfMissing(folder)) {
String b = in.toURI().relativize(folder.toURI()).getPath();
if (b.startsWith("/")) {
b = b.substring(1);
}
if (b.endsWith("/")) {
b = b.substring(0, b.length() - 1);
}
pools.put(b, new IrisJigsawPool());
}
});
findAllNBT(in, (folder, file) -> {
at.getAndIncrement();
String b = in.toURI().relativize(folder.toURI()).getPath();
if (b.startsWith("/")) {
b = b.substring(1);
}
if (b.endsWith("/")) {
b = b.substring(0, b.length() - 1);
}
IrisJigsawPool jpool = pools.get(b);
File destObjects = new File(out.getAbsolutePath() + "/objects/" + in.toURI().relativize(folder.toURI()).getPath());
File destPieces = new File(out.getAbsolutePath() + "/jigsaw-pieces/" + in.toURI().relativize(folder.toURI()).getPath());
destObjects.mkdirs();
destPieces.mkdirs();
try {
NamedTag tag = NBTUtil.read(file);
CompoundTag compound = (CompoundTag) tag.getTag();
if (compound.containsKey("blocks") && compound.containsKey("palette") && compound.containsKey("size")) {
String id = in.toURI().relativize(folder.toURI()).getPath() + file.getName().split("\\Q.\\E")[0];
@SuppressWarnings("unchecked") ListTag<IntTag> size = (ListTag<IntTag>) compound.getListTag("size");
int w = size.get(0).asInt();
int h = size.get(1).asInt();
int d = size.get(2).asInt();
KList<BlockData> palette = new KList<>();
@SuppressWarnings("unchecked") ListTag<CompoundTag> paletteList = (ListTag<CompoundTag>) compound.getListTag("palette");
for (int i = 0; i < paletteList.size(); i++) {
CompoundTag cp = paletteList.get(i);
palette.add(NBTWorld.getBlockData(cp));
}
IrisJigsawPiece piece = new IrisJigsawPiece();
IrisObject object = new IrisObject(w, h, d);
@SuppressWarnings("unchecked") ListTag<CompoundTag> blockList = (ListTag<CompoundTag>) compound.getListTag("blocks");
for (int i = 0; i < blockList.size(); i++) {
CompoundTag cp = blockList.get(i);
@SuppressWarnings("unchecked") ListTag<IntTag> pos = (ListTag<IntTag>) cp.getListTag("pos");
int x = pos.get(0).asInt();
int y = pos.get(1).asInt();
int z = pos.get(2).asInt();
BlockData bd = palette.get(cp.getInt("state")).clone();
if (bd.getMaterial().equals(Material.JIGSAW) && cp.containsKey("nbt")) {
piece.setObject(in.toURI().relativize(folder.toURI()).getPath() + file.getName().split("\\Q.\\E")[0]);
IrisPosition spos = new IrisPosition(object.getSigned(x, y, z));
CompoundTag nbt = cp.getCompoundTag("nbt");
CompoundTag finalState = new CompoundTag();
finalState.putString("Name", nbt.getString("final_state"));
BlockData jd = bd.clone();
bd = NBTWorld.getBlockData(finalState);
String joint = nbt.getString("joint");
String pool = nbt.getString("pool");
String poolId = toPoolName(pool);
String name = nbt.getString("name");
String target = nbt.getString("target");
pools.computeIfAbsent(poolId, (k) -> new IrisJigsawPool());
IrisJigsawPieceConnector connector = new IrisJigsawPieceConnector();
connector.setName(name);
connector.setTargetName(target);
connector.setRotateConnector(false);
connector.setPosition(spos);
connector.getPools().add(poolId);
connector.setDirection(IrisDirection.getDirection(((Jigsaw) jd).getOrientation()));
if (target.equals("minecraft:building_entrance")) {
connector.setInnerConnector(true);
}
piece.getConnectors().add(connector);
}
if (!bd.getMaterial().equals(Material.STRUCTURE_VOID) && !bd.getMaterial().equals(Material.AIR)) {
object.setUnsigned(x, y, z, bd);
}
}
jpool.getPieces().addIfMissing(id);
object.write(new File(destObjects, file.getName().split("\\Q.\\E")[0] + ".iob"));
IO.writeAll(new File(destPieces, file.getName().split("\\Q.\\E")[0] + ".json"), new JSONObject(new Gson().toJson(piece)).toString(4));
Iris.info("[Jigsaw]: (" + Form.pc((double) at.get() / (double) total.get(), 0) + ") Exported Piece: " + id);
}
} catch (Throwable e) {
e.printStackTrace();
Iris.reportError(e);
}
});
for (String i : pools.k()) {
try {
IO.writeAll(new File(destPools, i + ".json"), new JSONObject(new Gson().toJson(pools.get(i))).toString(4));
} catch (IOException e) {
e.printStackTrace();
Iris.reportError(e);
}
}
Iris.info("Done! Exported " + Form.f((total.get() * 2) + pools.size()) + " Files!");
}
public void findAllNBT(File path, Consumer2<File, File> inFile) {
private void findAllNBT(File path, KList<File> found) {
if (path == null) {
return;
}
if (path.isFile() && path.getName().endsWith(".nbt")) {
inFile.accept(path.getParentFile(), path);
found.add(path);
return;
}
for (File i : path.listFiles()) {
File[] children = path.listFiles();
if (children == null) {
return;
}
for (File i : children) {
if (i.isDirectory()) {
findAllNBT(i, inFile);
findAllNBT(i, found);
} else if (i.isFile() && i.getName().endsWith(".nbt")) {
inFile.accept(path, i);
found.add(i);
}
}
}
private void ensurePackMetadata(File datapackRoot) throws IOException {
File mcmeta = new File(datapackRoot, "pack.mcmeta");
if (mcmeta.exists()) {
return;
}
int format = INMS.get().getDataVersion().getPackFormat();
String meta = """
{
"pack": {
"description": "Iris loose structure ingestion pack",
"pack_format": {}
}
}
""".replace("{}", String.valueOf(format));
IO.writeAll(mcmeta, meta);
}
private int ingestLooseNbtStructures(File source, File datapackRoot) {
KList<File> nbtFiles = new KList<>();
findAllNBT(source, nbtFiles);
if (nbtFiles.isEmpty()) {
return 0;
}
File structureRoot = new File(datapackRoot, "data/iris_loose/structures");
structureRoot.mkdirs();
int copied = 0;
try {
ensurePackMetadata(datapackRoot);
for (File nbt : nbtFiles) {
String relative = source.toURI().relativize(nbt.toURI()).getPath();
if (relative == null || relative.isEmpty()) {
continue;
}
File output = new File(structureRoot, relative);
File parent = output.getParentFile();
if (parent != null) {
parent.mkdirs();
}
Files.copy(nbt.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
copied++;
}
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
return copied;
}
public void check(VolmitSender s) {
int m = 0;
Iris.instance.getDataFolder("convert");
for (File i : folder.listFiles()) {
File[] files = folder.listFiles();
if (files == null) {
s.sendMessage("Converted 0 Files");
return;
}
for (File i : files) {
for (Converter j : converters) {
if (i.getName().endsWith("." + j.getInExtension())) {
File out = new File(folder, i.getName().replaceAll("\\Q." + j.getInExtension() + "\\E", "." + j.getOutExtension()));
@@ -224,16 +146,13 @@ public class ConversionSVC implements IrisService {
s.sendMessage("Converted " + i.getName() + " -> " + out.getName());
}
}
}
if (i.isDirectory() && i.getName().equals("structures")) {
File f = new File(folder, "jigsaw");
if (!f.exists()) {
s.sendMessage("Converting NBT Structures into Iris Jigsaw Structures...");
f.mkdirs();
J.a(() -> convertStructures(i, f, s));
}
}
File loose = new File(folder, "structures");
if (loose.isDirectory()) {
File datapackRoot = Iris.instance.getDataFolder("datapacks", "iris-loose-structures");
int ingested = ingestLooseNbtStructures(loose, datapackRoot);
s.sendMessage("Ingested " + ingested + " loose NBT structure" + (ingested == 1 ? "" : "s") + " into " + datapackRoot.getName());
}
s.sendMessage("Converted " + m + " File" + (m == 1 ? "" : "s"));

View File

@@ -49,7 +49,6 @@ import art.arcane.volmlib.util.mantle.flag.MantleFlag;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.MatterStructurePOI;
import art.arcane.volmlib.util.matter.slices.container.JigsawStructureContainer;
import art.arcane.volmlib.util.scheduling.ChronoLatch;
import art.arcane.iris.util.common.scheduling.J;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
@@ -252,17 +251,6 @@ public class IrisEngine implements Engine {
return pois;
}
@Override
public IrisJigsawStructure getStructureAt(int x, int z) {
return getMantle().getJigsawComponent().guess(x, z);
}
@Override
public IrisJigsawStructure getStructureAt(int x, int y, int z) {
var container = getMantle().getMantle().get(x, y, z, JigsawStructureContainer.class);
return container == null ? null : getData().getJigsawStructureLoader().load(container.getLoadKey());
}
private void warmupChunk(int x, int z) {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {

View File

@@ -29,7 +29,6 @@ import art.arcane.iris.engine.mantle.EngineMantle;
import art.arcane.iris.engine.mantle.MantleComponent;
import art.arcane.iris.engine.mantle.components.MantleCarvingComponent;
import art.arcane.iris.engine.mantle.components.MantleFluidBodyComponent;
import art.arcane.iris.engine.mantle.components.MantleJigsawComponent;
import art.arcane.iris.engine.mantle.components.MantleObjectComponent;
import art.arcane.iris.util.project.matter.IrisMatterSupport;
import art.arcane.volmlib.util.collection.KList;
@@ -73,7 +72,6 @@ public class IrisEngineMantle implements EngineMantle {
private final AtomicCache<List<Pair<List<MantleComponent>, Integer>>> componentsCache = new AtomicCache<>();
private final AtomicCache<Set<MantleFlag>> disabledFlags = new AtomicCache<>();
private final MantleObjectComponent object;
private final MantleJigsawComponent jigsaw;
public IrisEngineMantle(Engine engine) {
this.engine = engine;
@@ -81,8 +79,6 @@ public class IrisEngineMantle implements EngineMantle {
components = new KMap<>();
registerComponent(new MantleCarvingComponent(this));
registerComponent(new MantleFluidBodyComponent(this));
jigsaw = new MantleJigsawComponent(this);
registerComponent(jigsaw);
object = new MantleObjectComponent(this);
registerComponent(object);
}
@@ -160,11 +156,6 @@ public class IrisEngineMantle implements EngineMantle {
return disabledFlags.aquire(() -> Set.copyOf(getDimension().getDisabledComponents()));
}
@Override
public MantleJigsawComponent getJigsawComponent() {
return jigsaw;
}
@Override
public MantleObjectComponent getObjectComponent() {
return object;

View File

@@ -63,7 +63,6 @@ public final class IrisNoisemapPrebakePipeline {
"generators",
"caves",
"ravines",
"jigsaw-structures",
"mods",
"expressions"
);
@@ -74,9 +73,6 @@ public final class IrisNoisemapPrebakePipeline {
"generators",
"caves",
"ravines",
"jigsaw-structures",
"jigsaw-pools",
"jigsaw-pieces",
"mods",
"expressions",
"scripts",
@@ -791,7 +787,6 @@ public final class IrisNoisemapPrebakePipeline {
seedManager.getDecorator(),
seedManager.getTerrain(),
seedManager.getSpawn(),
seedManager.getJigsaw(),
seedManager.getCarve(),
seedManager.getDeposit(),
seedManager.getPost(),

View File

@@ -59,7 +59,6 @@ import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
import art.arcane.volmlib.util.matter.MatterUpdate;
import art.arcane.volmlib.util.matter.slices.container.JigsawPieceContainer;
import art.arcane.iris.util.common.parallel.BurstExecutor;
import art.arcane.iris.util.common.parallel.MultiBurst;
import art.arcane.iris.util.common.reflect.KeyedType;
@@ -232,12 +231,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@ChunkCoordinates
Set<Pair<String, BlockPos>> getPOIsAt(int x, int z);
@ChunkCoordinates
IrisJigsawStructure getStructureAt(int x, int z);
@BlockCoordinates
IrisJigsawStructure getStructureAt(int x, int y, int z);
@BlockCoordinates
default IrisBiome getCaveBiome(int x, int z) {
return getComplex().getCaveBiomeStream().get(x, z);
@@ -588,7 +581,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getTarget().getBurster();
}
@Deprecated
default void clean() {
burst().lazy(() -> getMantle().trim(10));
}
@@ -848,13 +840,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
int id = Integer.parseInt(v[1]);
JigsawPieceContainer container = chunk.get(x & 15, y, z & 15, JigsawPieceContainer.class);
if (container != null) {
IrisJigsawPiece piece = getData().getJigsawPieceLoader().load(container.getLoadKey());
if (piece.getObject().equals(object))
return new PlacedObject(piece.getPlacementOptions(), getData().getObjectLoader().load(object), id, x, z);
}
IrisRegion region = getRegion(x, z);
for (IrisObjectPlacement i : region.getObjects()) {
@@ -880,25 +865,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
}
@Nullable
@BlockCoordinates
default Position2 getNearestStronghold(Position2 pos) {
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
if (p.isEmpty()) return null;
Position2 pr = null;
double d = Double.MAX_VALUE;
for (Position2 i : p) {
double dx = i.distance(pos);
if (dx < d) {
d = dx;
pr = i;
}
}
return pr;
}
default void gotoBiome(IrisBiome biome, Player player, boolean teleport) {
Set<String> regionKeys = getDimension()
.getAllRegions(this).stream()
@@ -917,57 +883,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
}
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
if (s.getLoadKey().equals(getDimension().getStronghold())) {
Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()));
if (pr == null) {
player.sendMessage(C.GOLD + "No strongholds in world.");
} else {
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
J.s(() -> player.teleport(ll));
}
return;
}
if (getDimension().getJigsawStructures().stream()
.map(IrisJigsawStructurePlacement::getStructure)
.collect(Collectors.toSet()).contains(s.getLoadKey())) {
Locator.jigsawStructure(s.getLoadKey()).find(player, teleport, "Structure " + s.getLoadKey());
} else {
Set<String> biomeKeys = getDimension().getAllBiomes(this).stream()
.filter((i) -> i.getJigsawStructures()
.stream()
.anyMatch((j) -> j.getStructure().equals(s.getLoadKey())))
.map(IrisRegistrant::getLoadKey)
.collect(Collectors.toSet());
Set<String> regionKeys = getDimension().getAllRegions(this).stream()
.filter((i) -> i.getAllBiomeIds().stream().anyMatch(biomeKeys::contains)
|| i.getJigsawStructures()
.stream()
.anyMatch((j) -> j.getStructure().equals(s.getLoadKey())))
.map(IrisRegistrant::getLoadKey)
.collect(Collectors.toSet());
Locator<IrisJigsawStructure> sl = Locator.jigsawStructure(s.getLoadKey());
Locator<IrisBiome> locator = (engine, chunk) -> {
if (biomeKeys.contains(getSurfaceBiome((chunk.getX() << 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey())) {
return sl.matches(engine, chunk);
} else if (regionKeys.contains(getRegion((chunk.getX() << 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey())) {
return sl.matches(engine, chunk);
}
return false;
};
if (!regionKeys.isEmpty()) {
locator.find(player, teleport, "Structure " + s.getLoadKey());
} else {
player.sendMessage(C.RED + s.getLoadKey() + " is not in any defined regions, biomes or dimensions!");
}
}
}
default void gotoObject(String s, Player player, boolean teleport) {
Set<String> biomeKeys = getDimension().getAllBiomes(this).stream()
.filter((i) -> i.getObjects().stream().anyMatch((f) -> f.getPlace().contains(s)))

View File

@@ -23,7 +23,6 @@ import art.arcane.iris.core.nms.container.BlockPos;
import art.arcane.iris.core.nms.container.Pair;
import art.arcane.iris.core.tools.IrisToolbelt;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisJigsawStructure;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.util.project.context.ChunkContext;
@@ -65,13 +64,6 @@ public interface Locator<T> {
return (e, c) -> e.getRegion((c.getX() << 4) + 8, (c.getZ() << 4) + 8).getLoadKey().equals(loadKey);
}
static Locator<IrisJigsawStructure> jigsawStructure(String loadKey) {
return (e, c) -> {
IrisJigsawStructure s = e.getStructureAt(c.getX(), c.getZ());
return s != null && s.getLoadKey().equals(loadKey);
};
}
static Locator<IrisObject> object(String loadKey) {
return (e, c) -> e.getObjectsAt(c.getX(), c.getZ()).contains(loadKey);
}

View File

@@ -1,7 +1,6 @@
package art.arcane.iris.engine.framework;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.engine.object.IrisJigsawStructure;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.math.Position2;
@@ -28,13 +27,6 @@ public interface ResultLocator<T> {
}
}
static ResultLocator<IrisJigsawStructure> locateStructure(Collection<String> keys) {
return (e, pos) -> {
var structure = e.getStructureAt(pos.getX(), pos.getZ());
return structure != null && keys.contains(structure.getLoadKey()) ? structure : null;
};
}
static ResultLocator<IrisObject> locateObject(Collection<String> keys) {
return (e, pos) -> {
Set<String> objects = e.getObjectsAt(pos.getX(), pos.getZ());

View File

@@ -46,7 +46,6 @@ public class SeedManager {
private final long decorator;
private final long terrain;
private final long spawn;
private final long jigsaw;
private final long carve;
private final long deposit;
private final long post;
@@ -71,7 +70,6 @@ public class SeedManager {
decorator = of("decorator");
terrain = of("terrain");
spawn = of("spawn");
jigsaw = of("jigsaw");
carve = of("carve");
deposit = of("deposit");
post = of("post");

View File

@@ -1,210 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.jigsaw;
import art.arcane.iris.Iris;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.object.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.iris.util.common.math.AxisAlignedBB;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import org.bukkit.util.BlockVector;
import java.util.ArrayList;
import java.util.List;
@Data
public class PlannedPiece {
private IrisPosition position;
private IrisObject object;
private IrisObject ogObject;
private IrisJigsawPiece piece;
private IrisObjectRotation rotation;
@EqualsAndHashCode.Exclude
private IrisData data;
private KList<IrisJigsawPieceConnector> connected;
private boolean dead = false;
private AxisAlignedBB box;
@EqualsAndHashCode.Exclude
private PlannedStructure structure;
@EqualsAndHashCode.Exclude
@Setter(AccessLevel.NONE)
private ParentConnection parent = null;
@EqualsAndHashCode.Exclude
@Setter(AccessLevel.NONE)
private KMap<IrisJigsawPieceConnector, IrisPosition> realPositions;
public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece) {
this(structure, position, piece, 0, 0, 0);
}
public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece, int rx, int ry, int rz) {
this(structure, position, piece, IrisObjectRotation.of(rx * 90D, ry * 90D, rz * 90D));
}
public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece, IrisObjectRotation rot) {
this.structure = structure;
this.position = position;
this.data = piece.getLoader();
this.setRotation(rot);
this.ogObject = data.getObjectLoader().load(piece.getObject());
this.object = structure.rotated(piece, rotation);
if (this.ogObject == null || this.object == null) {
throw new IllegalStateException("Unable to create planned piece for object \"" + piece.getObject() + "\"");
}
this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset()));
this.piece.setLoadKey(piece.getLoadKey());
this.object.setLoadKey(piece.getObject());
this.ogObject.setLoadKey(piece.getObject());
this.connected = new KList<>();
this.realPositions = new KMap<>();
}
public void setPosition(IrisPosition p) {
this.position = p;
box = null;
}
public String toString() {
return piece.getLoadKey() + "@(" + position.getX() + "," + position.getY() + "," + position.getZ() + ")[rot:" + rotation.toString() + "]";
}
public AxisAlignedBB getBox() {
if (box != null) {
return box;
}
BlockVector v = getObject().getCenter();
IrisPosition pos = new IrisPosition();
IrisObjectPlacement options = piece.getPlacementOptions();
if (options != null && options.getTranslate() != null) {
IrisObjectTranslate translate = options.getTranslate();
pos.setX(translate.getX());
pos.setY(translate.getY());
pos.setZ(translate.getZ());
}
box = object.getAABB().shifted(position.add(new IrisPosition(object.getCenter())).add(pos));
return box;
}
public boolean contains(IrisPosition p) {
return getBox().contains(p);
}
public boolean collidesWith(PlannedPiece p) {
return getBox().intersects(p.getBox());
}
public KList<IrisJigsawPieceConnector> getAvailableConnectors() {
if (connected.isEmpty()) {
return piece.getConnectors().copy();
}
if (connected.size() == piece.getConnectors().size()) {
return new KList<>();
}
KList<IrisJigsawPieceConnector> c = new KList<>();
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
if (!connected.contains(i)) {
c.add(i);
}
}
return c;
}
public KList<IrisJigsawPieceConnector> getChildConnectors() {
ParentConnection pc = getParent();
KList<IrisJigsawPieceConnector> c = getConnected().copy();
if (pc != null) c.removeIf(i -> i.equals(pc.connector));
return c;
}
public boolean connect(IrisJigsawPieceConnector c, PlannedPiece p, IrisJigsawPieceConnector pc) {
if (piece.getConnectors().contains(c) && p.getPiece().getConnectors().contains(pc)) {
if (connected.contains(c) || p.connected.contains(pc)) return false;
connected.add(c);
p.connected.add(pc);
p.parent = new ParentConnection(this, c, p, pc);
return true;
}
return false;
}
public IrisPosition getWorldPosition(IrisJigsawPieceConnector c) {
return getWorldPosition(c.getPosition());
}
public List<IrisPosition> getConnectorWorldPositions() {
List<IrisPosition> worldPositions = new ArrayList<>();
for (IrisJigsawPieceConnector connector : this.piece.getConnectors()) {
IrisPosition worldPosition = getWorldPosition(connector.getPosition());
worldPositions.add(worldPosition);
}
return worldPositions;
}
public IrisPosition getWorldPosition(IrisPosition position) {
return this.position.add(position).add(new IrisPosition(object.getCenter()));
}
public void debugPrintConnectorPositions() {
Iris.debug("Connector World Positions for PlannedPiece at " + position + ":");
List<IrisPosition> connectorPositions = getConnectorWorldPositions();
for (IrisPosition pos : connectorPositions) {
Iris.debug(" - Connector at: " + pos);
}
}
public boolean isFull() {
return connected.size() >= piece.getConnectors().size();
}
public void setRealPositions(int x, int y, int z, IObjectPlacer placer) {
boolean isUnderwater = piece.getPlacementOptions().isUnderwater();
for (IrisJigsawPieceConnector c : piece.getConnectors()) {
var pos = c.getPosition().add(new IrisPosition(x, 0, z));
if (y < 0) {
pos.setY(pos.getY() + placer.getHighest(pos.getX(), pos.getZ(), getData(), isUnderwater) + (object.getH() / 2));
} else {
pos.setY(pos.getY() + y);
}
realPositions.put(c, pos);
}
}
public record ParentConnection(PlannedPiece parent, IrisJigsawPieceConnector parentConnector, PlannedPiece self, IrisJigsawPieceConnector connector) {
public IrisPosition getTargetPosition() {
var pos = parent.realPositions.get(parentConnector);
if (pos == null) return null;
return pos.add(new IrisPosition(parentConnector.getDirection().toVector()))
.sub(connector.getPosition())
.sub(new IrisPosition(self.object.getCenter()));
}
}
}

View File

@@ -1,372 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.jigsaw;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import art.arcane.iris.Iris;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.placer.WorldObjectPlacer;
import art.arcane.iris.engine.object.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.slices.container.JigsawPieceContainer;
import art.arcane.volmlib.util.matter.slices.container.JigsawStructureContainer;
import art.arcane.volmlib.util.matter.slices.container.JigsawStructuresContainer;
import art.arcane.iris.util.common.scheduling.J;
import lombok.Data;
import org.bukkit.Axis;
import java.util.function.Consumer;
@Data
public class PlannedStructure {
private static ConcurrentLinkedHashMap<String, IrisObject> objectRotationCache
= new ConcurrentLinkedHashMap.Builder<String, IrisObject>()
.initialCapacity(64)
.maximumWeightedCapacity(1024)
.concurrencyLevel(32)
.build();
private KList<PlannedPiece> pieces;
private IrisJigsawStructure structure;
private IrisPosition position;
private IrisData data;
private RNG rng;
private boolean forcePlace;
private boolean verbose;
private boolean terminating;
public PlannedStructure(IrisJigsawStructure structure, IrisPosition position, RNG rng, boolean forcePlace) {
terminating = false;
verbose = true;
this.pieces = new KList<>();
this.structure = structure;
this.position = position;
this.rng = rng;
this.forcePlace = forcePlace || structure.isForcePlace();
this.data = structure.getLoader();
generateStartPiece();
for (int i = 0; i < structure.getMaxDepth(); i++) {
generateOutwards();
}
generateTerminators();
Iris.debug("JPlace: ROOT @ relative " + position.toString());
for (PlannedPiece i : pieces) {
Iris.debug("Place: " + i.getObject().getLoadKey() + " at @ relative " + i.getPosition().toString());
}
}
public boolean place(IObjectPlacer placer, Mantle<Matter> e, Engine eng) {
IrisObjectPlacement options = new IrisObjectPlacement();
options.setRotation(IrisObjectRotation.of(0,0,0));
int startHeight = pieces.get(0).getPosition().getY();
boolean placed = false;
for (PlannedPiece i : pieces) {
if (place(i, startHeight, options, placer, e, eng))
placed = true;
}
if (placed) {
Position2 chunkPos = new Position2(position.getX() >> 4, position.getZ() >> 4);
Position2 regionPos = new Position2(chunkPos.getX() >> 5, chunkPos.getZ() >> 5);
JigsawStructuresContainer slice = e.get(regionPos.getX(), 0, regionPos.getZ(), JigsawStructuresContainer.class);
if (slice == null) slice = new JigsawStructuresContainer();
slice.add(structure.getLoadKey(), chunkPos);
e.set(regionPos.getX(), 0, regionPos.getZ(), slice);
}
return placed;
}
public boolean place(PlannedPiece i, int startHeight, IrisObjectPlacement o, IObjectPlacer placer, Mantle<Matter> e, Engine eng) {
IrisObjectPlacement options = o;
if (i.getPiece().getPlacementOptions() != null) {
options = i.getPiece().getPlacementOptions();
options.getRotation().setEnabled(false);
options.setRotateTowardsSlope(false);
options.setWarp(new IrisGeneratorStyle(NoiseStyle.FLAT));
} else {
options.setMode(i.getPiece().getPlaceMode());
}
if (forcePlace) {
options.setForcePlace(true);
}
IrisObject v = i.getObject();
int sx = (v.getW() / 2);
int sz = (v.getD() / 2);
int xx = i.getPosition().getX() + sx;
int zz = i.getPosition().getZ() + sz;
int offset = i.getPosition().getY() - startHeight;
int height;
if (i.getStructure().getStructure().getLockY() == -1) {
if (i.getStructure().getStructure().getOverrideYRange() != null) {
height = (int) i.getStructure().getStructure().getOverrideYRange().get(rng, xx, zz, getData());
} else {
height = placer.getHighest(xx, zz, getData(), options.isUnderwater());
}
} else {
height = i.getStructure().getStructure().getLockY();
}
PlannedPiece.ParentConnection connection = i.getParent();
if (connection != null && connection.connector().isLockY()) {
var pos = connection.getTargetPosition();
if (pos != null) {
height = pos.getY();
offset = 0;
} else {
Iris.warn("Failed to get target position for " + v.getLoadKey());
}
}
height += offset + (v.getH() / 2);
if (options.getMode().equals(ObjectPlaceMode.PAINT)) {
height = -1;
}
int id = rng.i(0, Integer.MAX_VALUE);
JigsawPieceContainer piece = new JigsawPieceContainer(i.getPiece().getLoadKey());
JigsawStructureContainer structure = new JigsawStructureContainer(getStructure().getLoadKey());
i.setRealPositions(xx, height, zz, placer);
return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
placer.setData(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
placer.setData(b.getX(), b.getY(), b.getZ(), structure);
placer.setData(b.getX(), b.getY(), b.getZ(), piece);
}, null, getData().getEngine() != null ? getData() : eng.getData()) != -1;
}
public void place(WorldObjectPlacer placer, Consumer<Boolean> consumer) {
J.s(() -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine())));
}
private void generateOutwards() {
for (PlannedPiece i : getPiecesWithAvailableConnectors().shuffle(rng)) {
if (!generatePieceOutwards(i)) {
i.setDead(true);
}
}
}
private boolean generatePieceOutwards(PlannedPiece piece) {
boolean b = false;
for (IrisJigsawPieceConnector i : piece.getAvailableConnectors().shuffleCopy(rng)) {
if (generateConnectorOutwards(piece, i)) {
b = true;
piece.debugPrintConnectorPositions();
}
}
return b;
}
private boolean generateConnectorOutwards(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector) {
for (IrisJigsawPiece i : getShuffledPiecesFor(pieceConnector)) {
if (generateRotatedPiece(piece, pieceConnector, i)) {
return true;
}
}
return false;
}
private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea) {
if (!piece.getPiece().getPlacementOptions().getRotation().isEnabled()) {
return generateRotatedPiece(piece, pieceConnector, idea, 0, 0, 0);
}
KList<Integer> forder1 = new KList<Integer>().qadd(0).qadd(1).qadd(2).qadd(3).shuffle(rng);
KList<Integer> forder2 = new KList<Integer>().qadd(0).qadd(1).qadd(2).qadd(3).shuffle(rng);
for (Integer i : forder1) {
if (pieceConnector.isRotateConnector()) {
assert pieceConnector.getDirection().getAxis() != null;
if (!pieceConnector.getDirection().getAxis().equals(Axis.Y)) {
for (Integer j : forder2) {
if (pieceConnector.getDirection().getAxis().equals(Axis.X) && generateRotatedPiece(piece, pieceConnector, idea, j, i, 0)) {
return true;
}
if (pieceConnector.getDirection().getAxis().equals(Axis.Z) && generateRotatedPiece(piece, pieceConnector, idea, 0, i, j)) {
return true;
}
}
}
}
if (generateRotatedPiece(piece, pieceConnector, idea, 0, i, 0)) {
return true;
}
}
return false;
}
private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea, IrisObjectRotation rotation) {
if (!idea.getPlacementOptions().getRotation().isEnabled()) {
rotation = piece.getRotation();
}
PlannedPiece test = new PlannedPiece(this, piece.getPosition(), idea, rotation);
for (IrisJigsawPieceConnector j : test.getPiece().getConnectors().shuffleCopy(rng)) {
if (generatePositionedPiece(piece, pieceConnector, test, j)) {
return true;
}
}
return false;
}
private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea, int x, int y, int z) {
return generateRotatedPiece(piece, pieceConnector, idea, IrisObjectRotation.of(x * 90D, y * 90D, z * 90D));
}
private boolean generatePositionedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, PlannedPiece test, IrisJigsawPieceConnector testConnector) {
test.setPosition(new IrisPosition(0, 0, 0));
IrisPosition connector = piece.getWorldPosition(pieceConnector);
IrisDirection desiredDirection = pieceConnector.getDirection().reverse();
IrisPosition desiredPosition = connector.sub(new IrisPosition(desiredDirection.toVector()));
if (!pieceConnector.getTargetName().equals("*") && !pieceConnector.getTargetName().equals(testConnector.getName())) {
return false;
}
if (!testConnector.getDirection().equals(desiredDirection)) {
return false;
}
IrisPosition shift = test.getWorldPosition(testConnector);
test.setPosition(desiredPosition.sub(shift));
if (collidesWith(test, piece)) {
return false;
}
piece.connect(pieceConnector, test, testConnector);
pieces.add(test);
return true;
}
private KList<IrisJigsawPiece> getShuffledPiecesFor(IrisJigsawPieceConnector c) {
KList<IrisJigsawPiece> p = new KList<>();
KList<String> pools = terminating && getStructure().getTerminatePool() != null ? new KList<>(getStructure().getTerminatePool()) : c.getPools().shuffleCopy(rng);
for (String i : pools) {
for (String j : getData().getJigsawPoolLoader().load(i).getPieces().shuffleCopy(rng)) {
IrisJigsawPiece pi = getData().getJigsawPieceLoader().load(j);
if (pi == null || (terminating && !pi.isTerminal())) {
continue;
}
p.addIfMissing(pi);
}
}
return p.shuffle(rng);
}
private void generateStartPiece() {
pieces.add(new PlannedPiece(this, position, getData().getJigsawPieceLoader().load(rng.pick(getStructure().getPieces())), 0, getStructure().isDisableInitialRotation() ? 0 : rng.nextInt(4), 0));
}
private void generateTerminators() {
if (getStructure().isTerminate()) {
terminating = true;
generateOutwards();
}
}
public KList<PlannedPiece> getPiecesWithAvailableConnectors() {
KList<PlannedPiece> available = pieces.copy().removeWhere(PlannedPiece::isFull);
if (!terminating) available.removeIf(PlannedPiece::isDead);
return available;
}
public int getVolume() {
int v = 0;
for (PlannedPiece i : pieces) {
v += i.getObject().getH() * i.getObject().getW() * i.getObject().getD();
}
return v;
}
public int getMass() {
int v = 0;
for (PlannedPiece i : pieces) {
v += i.getObject().getBlocks().size();
}
return v;
}
public boolean collidesWith(PlannedPiece piece, PlannedPiece ignore) {
for (PlannedPiece i : pieces) {
if (i.equals(ignore)) {
continue;
}
if (i.collidesWith(piece)) {
return true;
}
}
return false;
}
public boolean contains(IrisPosition p) {
for (PlannedPiece i : pieces) {
if (i.contains(p)) {
return true;
}
}
return false;
}
public IrisObject rotated(IrisJigsawPiece piece, IrisObjectRotation rotation) {
String key = piece.getObject() + "-" + rotation.hashCode();
return objectRotationCache.computeIfAbsent(key, (k) -> {
IrisObject loaded = data.getObjectLoader().load(piece.getObject());
if (loaded == null) {
throw new IllegalStateException("Unable to load jigsaw object \"" + piece.getObject() + "\" for piece \"" + piece.getLoadKey() + "\"");
}
IrisObject rotatedObject = rotation.rotateCopy(loaded);
if (rotatedObject == null) {
throw new IllegalStateException("Unable to rotate jigsaw object \"" + piece.getObject() + "\" for piece \"" + piece.getLoadKey() + "\"");
}
return rotatedObject;
});
}
}

View File

@@ -24,7 +24,6 @@ import art.arcane.iris.core.nms.container.Pair;
import art.arcane.iris.engine.IrisComplex;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineTarget;
import art.arcane.iris.engine.mantle.components.MantleJigsawComponent;
import art.arcane.iris.engine.mantle.components.MantleObjectComponent;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisPosition;
@@ -114,12 +113,10 @@ public interface EngineMantle extends MatterGenerator {
return getComplex().getRoundedHeighteightStream().get(x, z);
}
@Deprecated(forRemoval = true)
default boolean isCarved(int x, int h, int z) {
return getMantle().get(x, h, z, MatterCavern.class) != null;
}
@Deprecated(forRemoval = true)
default BlockData get(int x, int y, int z) {
BlockData block = getMantle().get(x, y, z, BlockData.class);
if (block == null)
@@ -226,8 +223,6 @@ public interface EngineMantle extends MatterGenerator {
return getMantle().getLoadedRegionCount();
}
MantleJigsawComponent getJigsawComponent();
MantleObjectComponent getObjectComponent();
default boolean isCovered(int x, int z) {

View File

@@ -1,206 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.mantle.components;
import art.arcane.iris.engine.jigsaw.PlannedStructure;
import art.arcane.iris.engine.mantle.ComponentFlag;
import art.arcane.iris.engine.mantle.EngineMantle;
import art.arcane.iris.engine.mantle.IrisMantleComponent;
import art.arcane.iris.engine.mantle.MantleWriter;
import art.arcane.iris.engine.object.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.slices.container.JigsawStructuresContainer;
import art.arcane.iris.util.project.noise.CNG;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@ComponentFlag(ReservedFlag.JIGSAW)
public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, ReservedFlag.JIGSAW, 1);
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
}
@Override
public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) {
int xxx = 8 + (x << 4);
int zzz = 8 + (z << 4);
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz);
generateJigsaw(writer, x, z, biome, region);
}
@ChunkCoordinates
private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) {
long seed = cng.fit(Integer.MIN_VALUE, Integer.MAX_VALUE, x, z);
if (getDimension().getStronghold() != null) {
List<Position2> poss = getDimension().getStrongholds(seed());
if (poss != null) {
for (Position2 pos : poss) {
if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) {
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getDimension().getStronghold());
place(writer, new IrisPosition(pos.getX(), 23, pos.getZ()), structure, new RNG(seed), true);
return;
}
}
}
}
KSet<Position2> cachedRegions = new KSet<>();
KMap<String, KSet<Position2>> cache = new KMap<>();
KMap<Position2, Double> distanceCache = new KMap<>();
boolean placed = placeStructures(writer, seed, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache);
if (!placed)
placed = placeStructures(writer, seed, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache);
if (!placed)
placeStructures(writer, seed, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache);
}
@ChunkCoordinates
private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList<IrisJigsawStructurePlacement> structures,
KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
IrisJigsawStructurePlacement i = pick(structures, seed, x, z);
try {
if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
return false;
} catch (Throwable ignored) {}
RNG rng = new RNG(seed);
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
return place(writer, position, structure, rng, false);
}
@ChunkCoordinates
private boolean checkMinDistances(KMap<String, Integer> minDistances, int x, int z, KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
int range = 0;
for (int d : minDistances.values())
range = Math.max(range, d);
for (int xx = -range; xx <= range; xx++) {
for (int zz = -range; zz <= range; zz++) {
Position2 pos = new Position2((xx + x) >> 5, (zz + z) >> 5);
if (cachedRegions.contains(pos)) continue;
cachedRegions.add(pos);
JigsawStructuresContainer container = getMantle().get(pos.getX(), 0, pos.getZ(), JigsawStructuresContainer.class);
if (container == null) continue;
for (String key : container.getStructures()) {
KSet<Position2> positions = cache.computeIfAbsent(key, k -> new KSet<>());
container.getPositions(key).forEach(p -> positions.add(new Position2(p.getX(), p.getZ())));
}
}
}
Position2 pos = new Position2(x, z);
for (String structure : minDistances.keySet()) {
if (!cache.containsKey(structure)) continue;
double minDist = minDistances.get(structure);
minDist = minDist * minDist;
for (Position2 sPos : cache.get(structure)) {
double dist = distanceCache.computeIfAbsent(sPos, position2 -> position2.distance(pos));
if (minDist > dist) return true;
}
}
return false;
}
@ChunkCoordinates
public IrisJigsawStructure guess(int x, int z) {
// todo The guess doesnt bring into account that the placer may return -1
// todo doesnt bring skipped placements into account
long seed = cng.fit(Integer.MIN_VALUE, Integer.MAX_VALUE, x, z);
IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8);
IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8);
if (getDimension().getStronghold() != null) {
List<Position2> poss = getDimension().getStrongholds(seed());
if (poss != null) {
for (Position2 pos : poss) {
if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) {
return getData().getJigsawStructureLoader().load(getDimension().getStronghold());
}
}
}
}
IrisJigsawStructurePlacement i = pick(biome.getJigsawStructures(), seed, x, z);
if (i == null) i = pick(region.getJigsawStructures(), seed, x, z);
if (i == null) i = pick(getDimension().getJigsawStructures(), seed, x, z);
return i != null ? getData().getJigsawStructureLoader().load(i.getStructure()) : null;
}
@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());
}
@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());
}
private long jigsaw() {
return getEngineMantle().getEngine().getSeedManager().getJigsaw();
}
protected int computeRadius() {
var dimension = getDimension();
KSet<String> structures = new KSet<>();
if (dimension.getStronghold() != null) {
structures.add(dimension.getStronghold());
}
for (var placement : dimension.getJigsawStructures()) {
structures.add(placement.getStructure());
}
for (var region : dimension.getAllRegions(this::getData)) {
for (var placement : region.getJigsawStructures()) {
structures.add(placement.getStructure());
}
}
for (var biome : dimension.getAllBiomes(this::getData)) {
for (var placement : biome.getJigsawStructures()) {
structures.add(placement.getStructure());
}
}
int max = 0;
for (var structure : structures) {
max = Math.max(max, getData().getJigsawStructureLoader().load(structure).getMaxDimension());
}
return max;
}
}

View File

@@ -132,9 +132,6 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@ArrayType(min = 1, type = String.class)
@Desc("List any biome names (file names without.json) here as children. Portions of this biome can sometimes morph into their children. Iris supports cyclic relationships such as A > B > A > B. Iris will stop checking 9 biomes down the tree.")
private KList<String> children = new KList<>();
@ArrayType(min = 1, type = IrisJigsawStructurePlacement.class)
@Desc("Jigsaw structures")
private KList<IrisJigsawStructurePlacement> jigsawStructures = new KList<>();
@RegistryListResource(IrisBiome.class)
@Desc("The carving biome. If specified the biome will be used when under a carving instead of this current biome.")
private String carvingBiome = "";

View File

@@ -64,23 +64,19 @@ public class IrisDimension extends IrisRegistrant {
private final transient AtomicCache<CNG> rockLayerGenerator = new AtomicCache<>();
private final transient AtomicCache<CNG> fluidLayerGenerator = new AtomicCache<>();
private final transient AtomicCache<CNG> coordFracture = new AtomicCache<>();
private final transient AtomicCache<Double> sinr = new AtomicCache<>();
private final transient AtomicCache<Double> cosr = new AtomicCache<>();
private final transient AtomicCache<Double> rad = new AtomicCache<>();
private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>();
private final transient AtomicCache<KList<Position2>> strongholdsCache = new AtomicCache<>();
private final transient AtomicCache<KMap<String, KList<String>>> cachedPreProcessors = new AtomicCache<>();
private final transient AtomicCache<Double> sinr = new AtomicCache<>();
private final transient AtomicCache<Double> cosr = new AtomicCache<>();
private final transient AtomicCache<Double> rad = new AtomicCache<>();
private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>();
private final transient AtomicCache<KMap<String, KList<String>>> cachedPreProcessors = new AtomicCache<>();
@MinNumber(2)
@Required
@Desc("The human readable name of this dimension")
private String name = "A Dimension";
@MinNumber(1)
@MaxNumber(2032)
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeight = 256;
@RegistryListResource(IrisJigsawStructure.class)
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
private String stronghold;
@MinNumber(1)
@MaxNumber(2032)
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeight = 256;
@Desc("If set to true, Iris will remove chunks to allow visualizing cross sections of chunks easily")
private boolean debugChunkCrossSections = false;
@Desc("Vertically split up the biome palettes with 3 air blocks in between to visualize them")
@@ -91,14 +87,10 @@ public class IrisDimension extends IrisRegistrant {
@MaxNumber(16)
@Desc("Customize the palette height explosion")
private int explodeBiomePaletteSize = 3;
@MinNumber(2)
@MaxNumber(16)
@Desc("Every X/Z % debugCrossSectionsMod == 0 cuts the chunk")
private int debugCrossSectionsMod = 3;
@Desc("The average distance between strongholds")
private int strongholdJumpDistance = 1280;
@Desc("Define the maximum strongholds to place")
private int maxStrongholds = 14;
@MinNumber(2)
@MaxNumber(16)
@Desc("Every X/Z % debugCrossSectionsMod == 0 cuts the chunk")
private int debugCrossSectionsMod = 3;
@Desc("Tree growth override settings")
private IrisTreeSettings treeSettings = new IrisTreeSettings();
@Desc("Spawn Entities in this dimension over time. Iris will continually replenish these mobs just like vanilla does.")
@@ -149,16 +141,11 @@ public class IrisDimension extends IrisRegistrant {
private Boolean forceConvertTo320Height = false;
@Desc("The world environment")
private Environment environment = Environment.NORMAL;
@RegistryListResource(IrisRegion.class)
@Required
@ArrayType(min = 1, type = String.class)
@Desc("Define all of the regions to include in this dimension. Dimensions -> Regions -> Biomes -> Objects etc")
private KList<String> regions = new KList<>();
@ArrayType(min = 1, type = IrisJigsawStructurePlacement.class)
@Desc("Jigsaw structures")
private KList<IrisJigsawStructurePlacement> jigsawStructures = new KList<>();
@Desc("The jigsaw structure divisor to use when generating missing jigsaw placement values")
private double jigsawStructureDivisor = 18;
@RegistryListResource(IrisRegion.class)
@Required
@ArrayType(min = 1, type = String.class)
@Desc("Define all of the regions to include in this dimension. Dimensions -> Regions -> Biomes -> Objects etc")
private KList<String> regions = new KList<>();
@Required
@MinNumber(0)
@MaxNumber(1024)
@@ -289,25 +276,6 @@ public class IrisDimension extends IrisRegistrant {
return null;
}
public KList<Position2> getStrongholds(long seed) {
return strongholdsCache.aquire(() -> {
KList<Position2> pos = new KList<>();
int jump = strongholdJumpDistance;
RNG rng = new RNG((seed * 223) + 12945);
for (int i = 0; i < maxStrongholds + 1; i++) {
int m = i + 1;
pos.add(new Position2(
(int) ((rng.i(jump * i) + (jump * i)) * (rng.b() ? -1D : 1D)),
(int) ((rng.i(jump * i) + (jump * i)) * (rng.b() ? -1D : 1D))
));
}
pos.remove(0);
return pos;
});
}
public int getFluidHeight() {
return fluidHeight - (int) dimensionHeight.getMin();
}

View File

@@ -27,7 +27,6 @@ import art.arcane.volmlib.util.math.DOP;
import art.arcane.volmlib.util.math.VectorMath;
import org.bukkit.Axis;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Jigsaw;
import org.bukkit.util.Vector;
import java.util.Map;
@@ -78,27 +77,6 @@ public enum IrisDirection {
}
public static IrisDirection fromJigsawBlock(String direction) {
for (IrisDirection i : IrisDirection.values()) {
if (i.name().toLowerCase().split("\\Q_\\E")[0]
.equals(direction.split("\\Q_\\E")[0])) {
return i;
}
}
return null;
}
public static IrisDirection getDirection(Jigsaw.Orientation orientation) {
return switch (orientation) {
case DOWN_EAST, UP_EAST, EAST_UP -> EAST_POSITIVE_X;
case DOWN_NORTH, UP_NORTH, NORTH_UP -> NORTH_NEGATIVE_Z;
case DOWN_SOUTH, UP_SOUTH, SOUTH_UP -> SOUTH_POSITIVE_Z;
case DOWN_WEST, UP_WEST, WEST_UP -> WEST_NEGATIVE_X;
};
}
public static IrisDirection closest(Vector v) {
double m = Double.MAX_VALUE;
IrisDirection s = null;

View File

@@ -153,7 +153,7 @@ public class IrisEntity extends IrisRegistrant {
@Desc("The this entity is ageable, set it's baby status")
private boolean baby = false;
@Desc("If the entity should never be culled. Useful for Jigsaws")
@Desc("If the entity should never be culled.")
private boolean keepEntity = false;
@Desc("The surface type to spawn this mob on")

View File

@@ -1,29 +0,0 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.engine.object.annotations.Desc;
import art.arcane.iris.engine.object.annotations.MinNumber;
import art.arcane.iris.engine.object.annotations.RegistryListResource;
import art.arcane.iris.engine.object.annotations.Required;
import art.arcane.iris.engine.object.annotations.Snippet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Snippet("jigsaw-structure-min-distance")
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents the min distance between jigsaw structure placements")
@Data
public class IrisJigsawMinDistance {
@Required
@RegistryListResource(IrisJigsawStructure.class)
@Desc("The structure to check against")
private String structure;
@Required
@MinNumber(0)
@Desc("The min distance in blocks to a placed structure\nWARNING: The performance impact scales exponentially!")
private int distance;
}

View File

@@ -1,133 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.object;
import art.arcane.iris.Iris;
import art.arcane.iris.core.loader.IrisRegistrant;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.object.annotations.ArrayType;
import art.arcane.iris.engine.object.annotations.Desc;
import art.arcane.iris.engine.object.annotations.RegistryListResource;
import art.arcane.iris.engine.object.annotations.Required;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.iris.util.common.plugin.VolmitSender;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.util.BlockVector;
import java.io.IOException;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents a structure tile")
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisJigsawPiece extends IrisRegistrant {
@RegistryListResource(IrisObject.class)
@Required
@Desc("The object this piece represents")
private String object = "";
@ArrayType(type = IrisJigsawPieceConnector.class)
@Desc("The connectors this object contains")
private KList<IrisJigsawPieceConnector> connectors = new KList<>();
@Desc("Configure everything about the object placement. Please don't define this unless you actually need it as using this option will slow down the jigsaw deign stage. Use this where you need it, just avoid using it everywhere to keep things fast.")
private IrisObjectPlacement placementOptions = new IrisObjectPlacement().setMode(ObjectPlaceMode.FAST_MAX_HEIGHT);
private transient AtomicCache<Integer> max2dDim = new AtomicCache<>();
private transient AtomicCache<Integer> max3dDim = new AtomicCache<>();
public int getMax2dDimension() {
return max2dDim.aquire(() -> {
try {
BlockVector v = IrisObject.sampleSize(getLoader().getObjectLoader().findFile(getObject()));
return Math.max(v.getBlockX(), v.getBlockZ());
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
return 0;
});
}
public int getMax3dDimension() {
return max3dDim.aquire(() -> {
try {
BlockVector v = IrisObject.sampleSize(getLoader().getObjectLoader().findFile(getObject()));
return Math.max(Math.max(v.getBlockX(), v.getBlockZ()), v.getBlockY());
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
return -1;
});
}
public IrisJigsawPieceConnector getConnector(IrisPosition relativePosition) {
for (IrisJigsawPieceConnector i : connectors) {
if (i.getPosition().equals(relativePosition)) {
return i;
}
}
return null;
}
public IrisJigsawPiece copy() {
var gson = getLoader().getGson();
IrisJigsawPiece copy = gson.fromJson(gson.toJson(this), IrisJigsawPiece.class);
copy.setLoader(getLoader());
copy.setLoadKey(getLoadKey());
copy.setLoadFile(getLoadFile());
return copy;
}
public boolean isTerminal() {
return connectors.size() == 1;
}
public ObjectPlaceMode getPlaceMode() {
return getPlacementOptions().getMode();
}
@Override
public String getFolderName() {
return "jigsaw-pieces";
}
@Override
public String getTypeName() {
return "Jigsaw Piece";
}
@Override
public void scanForErrors(JSONObject p, VolmitSender sender) {
}
}

View File

@@ -1,101 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.object;
import art.arcane.iris.engine.object.annotations.*;
import art.arcane.volmlib.util.collection.KList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Snippet("connector")
@Desc("Represents a structure tile")
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisJigsawPieceConnector {
@Required
@Desc("The name of this connector, such as entry, or table node. This is a name for organization. Other connectors can specifically use targetName to target a specific connector type. Multiple connectors can use the same name.")
private String name = "";
@Required
@Desc("Target a piece's connector with the specified name. For any piece's connector, define * or don't define it.")
private String targetName = "*";
@Desc("Rotates the placed piece on this connector. If rotation is enabled, this connector will effectivley rotate, if this connector is facing the Z direction, then the connected piece would rotate in the X,Y direction in 90 degree segments.")
private boolean rotateConnector = false;
@Desc("If set to true, this connector is allowed to place pieces inside of it's own piece. For example if you are adding a light post, or house on top of a path piece, you would set this to true to allow the piece to collide with the path bounding box.")
private boolean innerConnector = false;
@RegistryListResource(IrisJigsawPool.class)
@Desc("Pick piece pools to place onto this connector")
@ArrayType(type = String.class, min = 1)
@Required
private KList<String> pools = new KList<>();
@RegistryListResource(IrisEntity.class)
@Desc("Pick an entity to spawn on this connector")
private String spawnEntity;
@Desc("Stop the entity from despawning")
private boolean keepEntity;
@MaxNumber(50)
@MinNumber(1)
@Desc("The amount of entities to spawn (must be a whole number)")
private int entityCount = 1;
@Desc("The relative position this connector is located at for connecting to other pieces")
@Required
private IrisPosition position = new IrisPosition(0, 0, 0);
@Desc("The relative position to this connector to place entities at")
@DependsOn({"spawnEntity"})
private IrisPosition entityPosition = null;
@Desc("The direction this connector is facing. If the direction is set to UP, then pieces will place ABOVE the connector.")
@Required
private IrisDirection direction = IrisDirection.UP_POSITIVE_Y;
@Desc("Lock the Y position of this connector")
private boolean lockY = false;
public String toString() {
return direction.getFace().name() + "@(" + position.getX() + "," + position.getY() + "," + position.getZ() + ")";
}
public IrisJigsawPieceConnector copy() {
IrisJigsawPieceConnector c = new IrisJigsawPieceConnector();
c.setInnerConnector(isInnerConnector());
c.setTargetName(getTargetName());
c.setPosition(getPosition().copy());
c.setDirection(getDirection());
c.setRotateConnector(isRotateConnector());
c.setName(getName());
c.setSpawnEntity(getSpawnEntity());
c.setPools(getPools().copy());
return c;
}
}

View File

@@ -1,63 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.object;
import art.arcane.iris.core.loader.IrisRegistrant;
import art.arcane.iris.engine.object.annotations.ArrayType;
import art.arcane.iris.engine.object.annotations.Desc;
import art.arcane.iris.engine.object.annotations.RegistryListResource;
import art.arcane.iris.engine.object.annotations.Required;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.iris.util.common.plugin.VolmitSender;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents a structure piece pool")
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisJigsawPool extends IrisRegistrant {
@RegistryListResource(IrisJigsawPiece.class)
@Required
@ArrayType(min = 1, type = String.class)
@Desc("A list of structure piece pools")
private KList<String> pieces = new KList<>();
@Override
public String getFolderName() {
return "jigsaw-pools";
}
@Override
public String getTypeName() {
return "Jigsaw Pool";
}
@Override
public void scanForErrors(JSONObject p, VolmitSender sender) {
}
}

View File

@@ -1,185 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.object;
import art.arcane.iris.Iris;
import art.arcane.iris.core.loader.IrisRegistrant;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.object.annotations.*;
import art.arcane.iris.engine.object.annotations.functions.StructureKeyFunction;
import art.arcane.iris.engine.object.annotations.functions.StructureKeyOrTagFunction;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.iris.util.common.plugin.VolmitSender;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents a jigsaw structure")
@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)
@Desc("The starting pieces. Randomly chooses a starting piece, then connects pieces using the pools define in the starting piece.")
private KList<String> pieces = new KList<>();
@MaxNumber(32)
@MinNumber(1)
@Desc("The maximum pieces that can step out from the center piece")
private int maxDepth = 9;
@Desc("Jigsaw grows the parallax layer which slows iris down a bit. Since there are so many pieces, Iris takes the avg piece size and calculates the parallax radius from that. Unless your structures are using only the biggest pieces, your structure should fit in the chosen size fine. If you are seeing cut-off parts of your structures or broken terrain, turn this option on. This option will pick the biggest piece dimensions and multiply it by your (maxDepth+1) * 2 as the size to grow the parallax layer by. But typically keep this off.")
private boolean useMaxPieceSizeForParallaxRadius = false;
@Desc("If set to true, iris will look for any pieces with only one connector in valid pools for edge connectors and attach them to 'terminate' the paths/piece connectors. Essentially it caps off ends. For example in a village, Iris would add houses to the ends of roads where possible. For terminators to be selected, they can only have one connector or they wont be chosen.")
private boolean terminate = true;
@RegistryListResource(IrisJigsawPool.class)
@Desc("The pool to use when terminating pieces")
private String terminatePool = null;
@Desc("Override the y range instead of placing on the height map")
private IrisStyledRange overrideYRange = null;
@Desc("Force Y to a specific value")
private int lockY = -1;
@Desc("Set to true to prevent rotating the initial structure piece")
private boolean disableInitialRotation = false;
@RegistryListFunction(StructureKeyOrTagFunction.class)
@Desc("The minecraft key to use when creating treasure maps")
private String structureKey = null;
@Desc("Force Place the whole structure")
private boolean forcePlace = false;
private transient AtomicCache<Integer> maxDimension = new AtomicCache<>();
private void loadPool(String p, KList<String> pools, KList<String> pieces) {
if (p.isEmpty()) {
return;
}
IrisJigsawPool pool = getLoader().getJigsawPoolLoader().load(p);
if (pool == null) {
Iris.warn("Can't find jigsaw pool: " + p);
return;
}
for (String i : pool.getPieces()) {
if (pieces.addIfMissing(i)) {
loadPiece(i, pools, pieces);
}
}
}
private void loadPiece(String p, KList<String> pools, KList<String> pieces) {
IrisJigsawPiece piece = getLoader().getJigsawPieceLoader().load(p);
if (piece == null) {
Iris.warn("Can't find jigsaw piece: " + p);
return;
}
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
for (String j : i.getPools()) {
if (pools.addIfMissing(j)) {
loadPool(j, pools, pieces);
}
}
}
}
public int getMaxDimension() {
return maxDimension.aquire(() -> {
if (datapackStructures.isNotEmpty()) {
return 0;
}
if (useMaxPieceSizeForParallaxRadius) {
int max = 0;
KList<String> pools = new KList<>();
KList<String> pieces = new KList<>();
for (String i : getPieces()) {
loadPiece(i, pools, pieces);
}
for (String i : pieces) {
max = Math.max(max, getLoader().getJigsawPieceLoader().load(i).getMax3dDimension());
}
return max * (((getMaxDepth() + 1) * 2) + 1);
} else {
KList<String> pools = new KList<>();
KList<String> pieces = new KList<>();
for (String i : getPieces()) {
loadPiece(i, pools, pieces);
}
if (pieces.isEmpty()) {
int max = 0;
for (String i : getPieces()) {
max = Math.max(max, getLoader().getJigsawPieceLoader().load(i).getMax2dDimension());
}
return max;
}
int avg = 0;
for (String i : pieces) {
avg += getLoader().getJigsawPieceLoader().load(i).getMax2dDimension();
}
return (avg / (!pieces.isEmpty() ? pieces.size() : 1)) * (((getMaxDepth() + 1) * 2) + 1);
}
});
}
@Override
public String getFolderName() {
return "jigsaw-structures";
}
@Override
public String getTypeName() {
return "Jigsaw Structure";
}
@Override
public void scanForErrors(JSONObject p, VolmitSender sender) {
}
}

View File

@@ -1,158 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.engine.object;
import art.arcane.iris.Iris;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.object.annotations.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.math.RNG;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Snippet("jigsaw-structure-placement")
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents a jigsaw structure placer")
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisJigsawStructurePlacement implements IRare {
@RegistryListResource(IrisJigsawStructure.class)
@Required
@Desc("The structure to place")
private String structure;
@Required
@Desc("The 1 in X chance rarity applies when generating multiple structures at once")
private int rarity = 100;
@Required
@DependsOn({"spacing", "separation"})
@Desc("The salt to use when generating the structure (to differentiate structures)")
@MinNumber(Long.MIN_VALUE)
@MaxNumber(Long.MAX_VALUE)
private long salt = 0;
@Required
@MinNumber(0)
@DependsOn({"salt", "separation"})
@Desc("Average distance in chunks between two neighboring generation attempts")
private int spacing = -1;
@Required
@MinNumber(0)
@DependsOn({"salt", "spacing"})
@Desc("Minimum distance in chunks between two neighboring generation attempts\nThe maximum distance of two neighboring generation attempts is 2*spacing - separation")
private int separation = -1;
@Desc("The method used to spread the structure")
private SpreadType spreadType = SpreadType.LINEAR;
@DependsOn({"spreadType"})
@Desc("The noise style to use when spreadType is set to 'NOISE'\nThis ignores the spacing and separation parameters")
private IrisGeneratorStyle style = new IrisGeneratorStyle();
@DependsOn({"spreadType", "style"})
@Desc("Threshold for noise style")
private double threshold = 0.5;
@ArrayType(type = IrisJigsawMinDistance.class)
@Desc("List of minimum distances to check for")
private KList<IrisJigsawMinDistance> minDistances = new KList<>();
public KMap<String, Integer> collectMinDistances() {
KMap<String, Integer> map = new KMap<>();
for (IrisJigsawMinDistance d : minDistances) {
map.compute(d.getStructure(), (k, v) -> v != null ? Math.min(toChunks(d.getDistance()), v) : toChunks(d.getDistance()));
}
return map;
}
private int toChunks(int blocks) {
return (int) Math.ceil(blocks / 16d);
}
private void calculateMissing(double divisor, long seed) {
if (salt != 0 && separation > 0 && spacing > 0)
return;
seed *= (long) structure.hashCode() * rarity;
if (salt == 0) {
salt = new RNG(seed).l(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
if (separation == -1 || spacing == -1) {
separation = (int) Math.round(rarity / divisor);
spacing = new RNG(seed).i(separation, separation * 2);
}
}
@ChunkCoordinates
public boolean shouldPlace(IrisData data, double divisor, long seed, int x, int z) {
calculateMissing(divisor, seed);
if (spreadType != SpreadType.NOISE)
return shouldPlaceSpread(seed, x, z);
return style.create(new RNG(seed + salt), data).noise(x, z) > threshold;
}
private boolean shouldPlaceSpread(long seed, int x, int z) {
if (separation > spacing) {
separation = spacing;
Iris.warn("JigsawStructurePlacement: separation must be less than or equal to spacing");
}
int i = Math.floorDiv(x, spacing);
int j = Math.floorDiv(z, spacing);
RNG rng = new RNG(i * 341873128712L + j * 132897987541L + seed + salt);
int k = spacing - separation;
int l = spreadType.apply(rng, k);
int m = spreadType.apply(rng, k);
return i * spacing + l == x && j * spacing + m == z;
}
@Desc("Spread type")
public enum SpreadType {
@Desc("Linear spread")
LINEAR(RNG::i),
@Desc("Triangular spread")
TRIANGULAR((rng, bound) -> (rng.i(bound) + rng.i(bound)) / 2),
@Desc("Noise based spread\nThis ignores the spacing and separation parameters")
NOISE((rng, bound) -> 0);
private final SpreadMethod method;
SpreadType(SpreadMethod method) {
this.method = method;
}
public int apply(RNG rng, int bound) {
return method.apply(rng, bound);
}
}
private interface SpreadMethod {
int apply(RNG rng, int bound);
}
}

View File

@@ -686,7 +686,7 @@ public class IrisObject extends IrisRegistrant {
boolean bail = false;
if (config.isFromBottom()) {
// todo Convert this to a mode and make it compatible with jigsaw
// todo Convert this to a dedicated mode.
y = (getH() + 1) + rty;
if (!config.isForcePlace()) {
if (placer.isCarved(x, y, z) ||

View File

@@ -32,7 +32,7 @@ import org.bukkit.block.data.BlockData;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents loot within this object or jigsaw piece")
@Desc("Represents loot within this object")
@Data
public class IrisObjectLoot implements IObjectLoot {
private final transient AtomicCache<KList<BlockData>> filterCache = new AtomicCache<>();

View File

@@ -101,21 +101,6 @@ public class IrisObjectRotation {
return e.rotateCopy(this);
}
public IrisJigsawPiece rotateCopy(IrisJigsawPiece v, IrisPosition offset) {
IrisJigsawPiece piece = v.copy();
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
i.setPosition(rotate(i.getPosition()).add(offset));
i.setDirection(rotate(i.getDirection()));
}
try {
var translate = piece.getPlacementOptions().getTranslate();
var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ())).add(offset);
translate.setX(pos.getX()).setY(pos.getY()).setZ(pos.getZ());
} catch (NullPointerException ignored) {}
return piece;
}
public BlockVector rotate(BlockVector direction) {
return rotate(direction, 0, 0, 0);
}

View File

@@ -15,7 +15,7 @@ import org.bukkit.block.data.BlockData;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents vanilla loot within this object or jigsaw piece")
@Desc("Represents vanilla loot within this object")
@Data
public class IrisObjectVanillaLoot implements IObjectLoot {
private final transient AtomicCache<KList<BlockData>> filterCache = new AtomicCache<>();

View File

@@ -71,9 +71,6 @@ public class IrisRegion extends IrisRegistrant implements IRare {
@Required
@Desc("The name of the region")
private String name = "A Region";
@ArrayType(min = 1, type = IrisJigsawStructurePlacement.class)
@Desc("Jigsaw structures")
private KList<IrisJigsawStructurePlacement> jigsawStructures = new KList<>();
@ArrayType(min = 1, type = IrisEffect.class)
@Desc("Effects are ambient effects such as potion effects, random sounds, or even particles around each player. All of these effects are played via packets so two players won't see/hear each others effects.\nDue to performance reasons, effects will play arround the player even if where the effect was played is no longer in the biome the player is in.")
private KList<IrisEffect> effects = new KList<>();

View File

@@ -1,74 +0,0 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineAssignedComponent;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.iris.util.project.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;
}
}

View File

@@ -643,7 +643,6 @@ public class Mth {
return Math.min(var0 * var0 * 0.6F + var1 * var1 * (3.0F + var1) / 4.0F + var2 * var2 * 0.8F, 1.0F);
}
@Deprecated
public static float rotlerp(float var0, float var1, float var2) {
float var3 = var1 - var0;
while (var3 < -180.0F)
@@ -653,7 +652,6 @@ public class Mth {
return var0 + var2 * var3;
}
@Deprecated
public static float rotWrap(double var0) {
while (var0 >= 180.0D)
var0 -= 360.0D;
@@ -709,4 +707,4 @@ public class Mth {
public static double length(int var0, double var1, int var3) {
return Math.sqrt((var0 * var0) + var1 * var1 + (var3 * var3));
}
}
}

View File

@@ -1,33 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.util.common.director.handlers;
import art.arcane.iris.engine.object.IrisJigsawPiece;
import art.arcane.iris.util.common.director.specialhandlers.RegistrantHandler;
public class JigsawPieceHandler extends RegistrantHandler<IrisJigsawPiece> {
public JigsawPieceHandler() {
super(IrisJigsawPiece.class, true);
}
@Override
public String getRandomDefault() {
return "jigsaw-piece";
}
}

View File

@@ -1,33 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.util.common.director.handlers;
import art.arcane.iris.engine.object.IrisJigsawPool;
import art.arcane.iris.util.common.director.specialhandlers.RegistrantHandler;
public class JigsawPoolHandler extends RegistrantHandler<IrisJigsawPool> {
public JigsawPoolHandler() {
super(IrisJigsawPool.class, true);
}
@Override
public String getRandomDefault() {
return "jigsaw-pool";
}
}

View File

@@ -1,33 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.util.common.director.handlers;
import art.arcane.iris.engine.object.IrisJigsawStructure;
import art.arcane.iris.util.common.director.specialhandlers.RegistrantHandler;
public class JigsawStructureHandler extends RegistrantHandler<IrisJigsawStructure> {
public JigsawStructureHandler() {
super(IrisJigsawStructure.class, true);
}
@Override
public String getRandomDefault() {
return "jigsaw-structure";
}
}

View File

@@ -12,7 +12,6 @@ import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;
@Deprecated(since = "3.7.1")
public class OldEnum {
private static final Class<?> oldEnum;

View File

@@ -338,7 +338,6 @@ public interface ProceduralStream<T> extends ProceduralLayer, Interpolated<T> {
}
@SuppressWarnings("unchecked")
@Deprecated(forRemoval = true)
default <V> ProceduralStream<V> selectRarity(V... types) {
KList<V> rarityTypes = new KList<>();
int totalRarity = 0;

View File

@@ -3,17 +3,8 @@ package art.arcane.iris.core.nms.v1_21_R7;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.MapCodec;
import art.arcane.iris.Iris;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.ResultLocator;
import art.arcane.iris.engine.framework.WrongEngineBroException;
import art.arcane.iris.engine.object.IrisJigsawStructure;
import art.arcane.iris.engine.object.IrisJigsawStructurePlacement;
import art.arcane.iris.engine.object.IrisStructurePopulator;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.iris.util.common.reflect.WrappedField;
import art.arcane.iris.util.common.reflect.WrappedReturningMethod;
import net.minecraft.CrashReport;
@@ -25,8 +16,6 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.Identifier;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedList;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.*;
@@ -42,14 +31,11 @@ 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_R7.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R7.generator.CustomChunkGenerator;
import org.bukkit.craftbukkit.v1_21_R7.generator.structure.CraftStructure;
import org.bukkit.event.world.AsyncStructureSpawnEvent;
import org.spigotmc.SpigotWorldConfig;
import javax.annotation.Nullable;
@@ -57,7 +43,6 @@ import java.lang.reflect.Field;
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 {
@@ -65,105 +50,19 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
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<>();
addAll(dimension.getJigsawStructures(), placements);
for (var region : dimension.getAllRegions(engine)) {
addAll(region.getJigsawStructures(), placements);
for (var biome : region.getAllBiomes(engine))
addAll(biome.getJigsawStructures(), placements);
}
var stronghold = dimension.getStronghold();
if (stronghold != null)
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
placements.removeIf(Objects::isNull);
var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow();
for (var s : placements) {
try {
String raw = s.getStructureKey();
if (raw == null) continue;
boolean tag = raw.startsWith("#");
if (tag) raw = raw.substring(1);
var location = Identifier.parse(raw);
if (!tag) {
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
continue;
}
var key = TagKey.create(Registries.STRUCTURE, location);
var set = registry.get(key).orElse(null);
if (set == null) {
Iris.error("Could not find structure tag: " + raw);
continue;
}
for (var holder : set) {
var resourceKey = holder.unwrapKey().orElse(null);
if (resourceKey == null) continue;
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
}
} catch (Throwable e) {
Iris.error("Failed to load structure: " + s.getLoadKey());
e.printStackTrace();
}
}
}
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
if (placements == null) return;
placements.stream()
.map(IrisJigsawStructurePlacement::getStructure)
.map(engine.getData().getJigsawStructureLoader()::load)
.filter(Objects::nonNull)
.forEach(structures::add);
}
@Override
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
if (holders.size() == 0) return null;
if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
}
if (engine.getDimension().isDisableExplorerMaps())
return null;
KMap<String, Holder<Structure>> structures = new KMap<>();
for (var holder : holders) {
if (holder == null) continue;
var key = holder.unwrapKey().orElse(null);
var set = this.structures.get(key);
if (set == null) continue;
for (var structure : set) {
structures.put(structure, holder);
}
}
if (structures.isEmpty())
return null;
var locator = ResultLocator.locateStructure(structures.keySet())
.then((e, p , s) -> structures.get(s.getLoadKey()));
if (findUnexplored)
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
try {
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
if (result == null) return null;
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
return Pair.of(blockPos, result.obj());
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
return null;
}
return delegate.findNearestMapStructure(level, holders, pos, radius, findUnexplored);
}
@Override
@@ -192,58 +91,10 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
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 = Identifier.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;
if (!IrisSettings.get().getGeneral().isAutoGenerateIntrinsicStructures()) {
return;
}
delegate.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
}
@Override

View File

@@ -10,7 +10,6 @@ import art.arcane.iris.core.nms.container.BlockProperty;
import art.arcane.iris.core.nms.datapack.DataVersion;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisJigsawStructurePlacement;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.iris.util.project.agent.Agent;
import art.arcane.volmlib.util.collection.KList;
@@ -814,8 +813,8 @@ public class NMSBinding implements INMSBinding {
.separation(random.separation())
.spacing(random.spacing())
.spreadType(switch (random.spreadType()) {
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
case LINEAR -> StructurePlacement.SpreadType.LINEAR;
case TRIANGULAR -> StructurePlacement.SpreadType.TRIANGULAR;
});
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
builder = StructurePlacement.ConcentricRings.builder()