Merge branch 'mca' of https://github.com/RePixelatedMC/Iris into PixelatedDev

This commit is contained in:
RePixelatedMC
2024-05-16 16:55:51 +02:00
14 changed files with 425 additions and 286 deletions

View File

@@ -247,7 +247,7 @@ allprojects {
compileOnly 'it.unimi.dsi:fastutil:8.5.8'
compileOnly 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2'
compileOnly 'org.zeroturnaround:zt-zip:1.14'
compileOnly 'com.google.code.gson:gson:2.9.0'
compileOnly 'com.google.code.gson:gson:2.10.1'
compileOnly 'org.ow2.asm:asm:9.2'
compileOnly 'com.google.guava:guava:33.0.0-jre'
compileOnly 'bsf:bsf:2.4.0'

View File

@@ -21,6 +21,7 @@ package com.volmit.iris.core.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.edit.JigsawEditor;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.placer.WorldObjectPlacer;
import com.volmit.iris.engine.jigsaw.PlannedStructure;
import com.volmit.iris.engine.object.IrisJigsawPiece;
import com.volmit.iris.engine.object.IrisJigsawStructure;
@@ -56,10 +57,16 @@ public class CommandJigsaw implements DecreeExecutor {
IrisJigsawStructure structure
) {
PrecisionStopwatch p = PrecisionStopwatch.start();
PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation()), new RNG());
VolmitSender sender = sender();
sender.sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2));
ps.place(world(), failed -> sender.sendMessage(failed ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place the structure!"));
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());
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());
}
}
@Decree(description = "Create a jigsaw piece")

View File

@@ -80,6 +80,7 @@ import java.nio.file.Files;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
@@ -347,6 +348,63 @@ public class CommandStudio implements DecreeExecutor {
player().openInventory(inv);
}
@Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER)
public void distances(@Param(description = "The radius") 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", Thread.MIN_PRIORITY);
var executor = multiBurst.burst(radius * radius);
sender.sendMessage(C.GRAY + "Generating data...");
var loc = player().getLocation();
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));
}
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
executor.complete();
multiBurst.close();
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!");
}
}
@Decree(description = "Render a world map (External GUI)", aliases = "render")
public void map() {
if (noGUI()) return;

View File

@@ -0,0 +1,127 @@
package com.volmit.iris.engine.framework.placer;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.IrisLootEvent;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.object.IObjectPlacer;
import com.volmit.iris.engine.object.InventorySlotType;
import com.volmit.iris.engine.object.IrisLootTable;
import com.volmit.iris.engine.object.TileData;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.math.RNG;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.InventoryHolder;
@Getter
@EqualsAndHashCode(exclude = {"engine", "mantle"})
public class WorldObjectPlacer implements IObjectPlacer {
private final World world;
private final Engine engine;
private final EngineMantle mantle;
public WorldObjectPlacer(World world) {
var a = IrisToolbelt.access(world);
if (a == null || a.getEngine() == null) throw new IllegalStateException(world.getName() + " is not an Iris World!");
this.world = world;
this.engine = a.getEngine();
this.mantle = engine.getMantle();
}
@Override
public int getHighest(int x, int z, IrisData data) {
return mantle.getHighest(x, z, data);
}
@Override
public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
return mantle.getHighest(x, z, data, ignoreFluid);
}
@Override
public void set(int x, int y, int z, BlockData d) {
Block block = world.getBlockAt(x, y + world.getMinHeight(), z);
if (y <= world.getMinHeight() || block.getType() == Material.BEDROCK) return;
InventorySlotType slot = null;
if (B.isStorageChest(d)) {
slot = InventorySlotType.STORAGE;
}
if (slot != null) {
RNG rx = new RNG(Cache.key(x, z));
KList<IrisLootTable> tables = engine.getLootTables(rx, block);
try {
Bukkit.getPluginManager().callEvent(new IrisLootEvent(engine, block, slot, tables));
if (!tables.isEmpty()){
Iris.debug("IrisLootEvent has been accessed");
}
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState();
engine.addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
} catch (Throwable e) {
Iris.reportError(e);
}
}
block.setBlockData(d);
}
@Override
public BlockData get(int x, int y, int z) {
return world.getBlockAt(x, y + world.getMinHeight(), z).getBlockData();
}
@Override
public boolean isPreventingDecay() {
return mantle.isPreventingDecay();
}
@Override
public boolean isCarved(int x, int y, int z) {
return mantle.isCarved(x, y, z);
}
@Override
public boolean isSolid(int x, int y, int z) {
return world.getBlockAt(x, y + world.getMinHeight(), z).getType().isSolid();
}
@Override
public boolean isUnderwater(int x, int z) {
return mantle.isUnderwater(x, z);
}
@Override
public int getFluidHeight() {
return mantle.getFluidHeight();
}
@Override
public boolean isDebugSmartBore() {
return mantle.isDebugSmartBore();
}
@Override
public void setTile(int xx, int yy, int zz, TileData<? extends TileState> tile) {
BlockState state = world.getBlockAt(xx, yy + world.getMinHeight(), zz).getState();
tile.toBukkitTry(state);
state.update();
}
}

View File

@@ -20,30 +20,19 @@ package com.volmit.iris.engine.jigsaw;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.math.AxisAlignedBB;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.RNG;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.InventoryHolder;
import lombok.Setter;
import org.bukkit.util.BlockVector;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("ALL")
@Data
public class PlannedPiece {
private IrisPosition position;
@@ -58,6 +47,12 @@ public class PlannedPiece {
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);
@@ -79,6 +74,7 @@ public class PlannedPiece {
this.object.setLoadKey(piece.getObject());
this.ogObject.setLoadKey(piece.getObject());
this.connected = new KList<>();
this.realPositions = new KMap<>();
}
@@ -97,7 +93,15 @@ public class PlannedPiece {
}
BlockVector v = getObject().getCenter();
box = object.getAABB().shifted(position.add(new IrisPosition(object.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;
}
@@ -129,11 +133,21 @@ public class PlannedPiece {
return c;
}
public boolean connect(IrisJigsawPieceConnector c) {
if (piece.getConnectors().contains(c)) {
return connected.addIfMissing(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;
}
@@ -165,105 +179,29 @@ public class PlannedPiece {
}
public boolean isFull() {
return connected.size() >= piece.getConnectors().size() || isDead();
return connected.size() >= piece.getConnectors().size();
}
public boolean place(World world) {
PlatformChunkGenerator a = IrisToolbelt.access(world);
int minY = 0;
if (a != null) {
minY = a.getEngine().getMinHeight();
if (!a.getEngine().getDimension().isBedrock())
minY--; //If the dimension has no bedrock, allow it to go a block lower
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);
}
Engine engine = a != null ? a.getEngine() : IrisContext.get().getEngine();
}
getPiece().getPlacementOptions().setTranslate(new IrisObjectTranslate());
getPiece().getPlacementOptions().getRotation().setEnabled(false);
getPiece().getPlacementOptions().setRotateTowardsSlope(false);
int finalMinY = minY;
RNG rng = getStructure().getRng().nextParallelRNG(37555);
// TODO: REAL CLASSES!!!!!!!
return getObject().place(position.getX() + getObject().getCenter().getBlockX(), position.getY() + getObject().getCenter().getBlockY(), position.getZ() + getObject().getCenter().getBlockZ(), new IObjectPlacer() {
@Override
public int getHighest(int x, int z, IrisData data) {
return position.getY();
}
@Override
public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
return position.getY();
}
@Override
public void set(int x, int y, int z, BlockData d) {
Block block = world.getBlockAt(x, y, z);
//Prevent blocks being set in or bellow bedrock
if (y <= finalMinY || block.getType() == Material.BEDROCK) return;
block.setBlockData(d);
if (a != null && getPiece().getPlacementOptions().getLoot().isNotEmpty() &&
block.getState() instanceof InventoryHolder) {
IrisLootTable table = getPiece().getPlacementOptions().getTable(block.getBlockData(), getData());
if (table == null) return;
engine.addItems(false, ((InventoryHolder) block.getState()).getInventory(),
rng.nextParallelRNG(BlockPosition.toLong(x, y, z)),
new KList<>(table), InventorySlotType.STORAGE, x, y, z, 15);
}
}
@Override
public BlockData get(int x, int y, int z) {
return world.getBlockAt(x, y, z).getBlockData();
}
@Override
public boolean isPreventingDecay() {
return false;
}
@Override
public boolean isCarved(int x, int y, int z) {
return false;
}
@Override
public boolean isSolid(int x, int y, int z) {
return world.getBlockAt(x, y, z).getType().isSolid();
}
@Override
public boolean isUnderwater(int x, int z) {
return false;
}
@Override
public int getFluidHeight() {
return 0;
}
@Override
public boolean isDebugSmartBore() {
return false;
}
@Override
public void setTile(int xx, int yy, int zz, TileData<? extends TileState> tile) {
BlockState state = world.getBlockAt(xx, yy, zz).getState();
tile.toBukkitTry(state);
state.update();
}
@Override
public Engine getEngine() {
return engine;
}
}, piece.getPlacementOptions(), rng, getData().getEngine() == null ? engine.getData() : getData()) != -1;
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

@@ -21,9 +21,8 @@ package com.volmit.iris.engine.jigsaw;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.placer.WorldObjectPlacer;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.mantle.Mantle;
@@ -34,12 +33,6 @@ import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.scheduling.J;
import lombok.Data;
import org.bukkit.Axis;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import java.util.function.Consumer;
@@ -110,6 +103,7 @@ public class PlannedStructure {
options = i.getPiece().getPlacementOptions();
options.getRotation().setEnabled(false);
options.setRotateTowardsSlope(false);
options.setWarp(new IrisGeneratorStyle(NoiseStyle.FLAT));
} else {
options.setMode(i.getPiece().getPlaceMode());
}
@@ -119,7 +113,6 @@ public class PlannedStructure {
int sz = (v.getD() / 2);
int xx = i.getPosition().getX() + sx;
int zz = i.getPosition().getZ() + sz;
RNG rngf = new RNG(Cache.key(xx, zz));
int offset = i.getPosition().getY() - startHeight;
int height;
@@ -133,6 +126,17 @@ public class PlannedStructure {
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)) {
@@ -141,89 +145,15 @@ public class PlannedStructure {
int id = rng.i(0, Integer.MAX_VALUE);
JigsawPieceContainer container = JigsawPieceContainer.toContainer(i.getPiece());
i.setRealPositions(xx, height, zz, placer);
return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
e.set(b.getX(), b.getY(), b.getZ(), container);
}, null, getData().getEngine() != null ? getData() : eng.getData()) != -1;
}
public void place(World world, Consumer<Boolean> consumer) {
var a = IrisToolbelt.access(world);
if (a == null || a.getEngine() == null) {
consumer.accept(null);
return;
}
var engine = a.getEngine();
var engineMantle = engine.getMantle();
var placer = new IObjectPlacer() {
@Override
public int getHighest(int x, int z, IrisData data) {
return engineMantle.getHighest(x, z, data);
}
@Override
public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
return engineMantle.getHighest(x, z, data, ignoreFluid);
}
@Override
public void set(int x, int y, int z, BlockData d) {
Block block = world.getBlockAt(x, y + world.getMinHeight(), z);
//Prevent blocks being set in or bellow bedrock
if (y <= world.getMinHeight() || block.getType() == Material.BEDROCK) return;
block.setBlockData(d);
}
@Override
public BlockData get(int x, int y, int z) {
return world.getBlockAt(x, y + world.getMinHeight(), z).getBlockData();
}
@Override
public boolean isPreventingDecay() {
return engineMantle.isPreventingDecay();
}
@Override
public boolean isCarved(int x, int y, int z) {
return engineMantle.isCarved(x, y, z);
}
@Override
public boolean isSolid(int x, int y, int z) {
return world.getBlockAt(x, y + world.getMinHeight(), z).getType().isSolid();
}
@Override
public boolean isUnderwater(int x, int z) {
return engineMantle.isUnderwater(x, z);
}
@Override
public int getFluidHeight() {
return engineMantle.getFluidHeight();
}
@Override
public boolean isDebugSmartBore() {
return engineMantle.isDebugSmartBore();
}
@Override
public void setTile(int xx, int yy, int zz, TileData<? extends TileState> tile) {
BlockState state = world.getBlockAt(xx, yy + world.getMinHeight(), zz).getState();
tile.toBukkitTry(state);
state.update();
}
@Override
public Engine getEngine() {
return engine;
}
};
J.s(() -> consumer.accept(place(placer, engineMantle.getMantle(), engine)));
public void place(WorldObjectPlacer placer, Consumer<Boolean> consumer) {
J.s(() -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine())));
}
private void generateOutwards() {
@@ -330,8 +260,7 @@ public class PlannedStructure {
return false;
}
piece.connect(pieceConnector);
test.connect(testConnector);
piece.connect(pieceConnector, test, testConnector);
pieces.add(test);
return true;
@@ -340,7 +269,8 @@ public class PlannedStructure {
private KList<IrisJigsawPiece> getShuffledPiecesFor(IrisJigsawPieceConnector c) {
KList<IrisJigsawPiece> p = new KList<>();
for (String i : c.getPools().shuffleCopy(rng)) {
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);
@@ -366,7 +296,9 @@ public class PlannedStructure {
}
public KList<PlannedPiece> getPiecesWithAvailableConnectors() {
return pieces.copy().removeWhere(PlannedPiece::isFull);
KList<PlannedPiece> available = pieces.copy().removeWhere(PlannedPiece::isFull);
if (!terminating) available.removeIf(PlannedPiece::isDead);
return available;
}
public int getVolume() {

View File

@@ -18,7 +18,6 @@
package com.volmit.iris.engine.mantle.components;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.jigsaw.PlannedStructure;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.IrisMantleComponent;
@@ -35,34 +34,31 @@ import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.noise.NoiseType;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, MantleFlag.JIGSAW);
}
private RNG applyNoise(int x, int z) {
long seed = Cache.key(x, z) + getEngineMantle().getEngine().getSeedManager().getJigsaw();
CNG cng = CNG.signatureFast(new RNG(seed), NoiseType.WHITE, NoiseType.GLOB);
return new RNG((long) (seed * cng.noise(x, z)));
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
}
@Override
public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) {
RNG rng = applyNoise(x, z);
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, rng, x, z, biome, region);
generateJigsaw(writer, x, z, biome, region);
}
@ChunkCoordinates
private void generateJigsaw(MantleWriter writer, RNG rng, int x, int z, IrisBiome biome, IrisRegion region) {
private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) {
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
if (getDimension().getStronghold() != null) {
List<Position2> poss = getDimension().getStrongholds(seed());
@@ -70,7 +66,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
for (Position2 pos : poss) {
if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) {
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getDimension().getStronghold());
place(writer, pos.toIris(), structure, rng);
place(writer, pos.toIris(), structure, new RNG(seed));
return;
}
}
@@ -80,27 +76,23 @@ public class MantleJigsawComponent extends IrisMantleComponent {
KSet<Position2> cachedRegions = new KSet<>();
KMap<String, KSet<Position2>> cache = new KMap<>();
KMap<Position2, Double> distanceCache = new KMap<>();
boolean placed = placeStructures(writer, rng, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache);
boolean placed = placeStructures(writer, seed, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache);
if (!placed)
placed = placeStructures(writer, rng, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache);
placed = placeStructures(writer, seed, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache);
if (!placed)
placeStructures(writer, rng, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache);
placeStructures(writer, seed, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache);
}
@ChunkCoordinates
private boolean placeStructures(MantleWriter writer, RNG rng, int x, int z, KList<IrisJigsawStructurePlacement> structures,
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) {
for (IrisJigsawStructurePlacement i : structures) {
if (rng.nextInt(i.getRarity()) == 0) {
if (checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
continue;
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
if (place(writer, position, structure, rng))
return true;
}
}
return false;
IrisJigsawStructurePlacement i = pick(structures, seed, x, z);
if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
return false;
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);
}
@ChunkCoordinates
@@ -138,7 +130,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
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
RNG rng = applyNoise(x, z);
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_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);
@@ -154,29 +146,26 @@ public class MantleJigsawComponent extends IrisMantleComponent {
}
}
for (IrisJigsawStructurePlacement i : biome.getJigsawStructures()) {
if (rng.nextInt(i.getRarity()) == 0) {
return getData().getJigsawStructureLoader().load(i.getStructure());
}
}
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;
}
for (IrisJigsawStructurePlacement i : region.getJigsawStructures()) {
if (rng.nextInt(i.getRarity()) == 0) {
return getData().getJigsawStructureLoader().load(i.getStructure());
}
}
for (IrisJigsawStructurePlacement i : getDimension().getJigsawStructures()) {
if (rng.nextInt(i.getRarity()) == 0) {
return getData().getJigsawStructureLoader().load(i.getStructure());
}
}
return null;
@Nullable
@ChunkCoordinates
private IrisJigsawStructurePlacement pick(List<IrisJigsawStructurePlacement> structures, long seed, int x, int z) {
return IRare.pick(structures.stream()
.filter(p -> p.shouldPlace(jigsaw(), x, z))
.toList(), new RNG(seed).nextDouble());
}
@BlockCoordinates
private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) {
return new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine());
}
private long jigsaw() {
return getEngineMantle().getEngine().getSeedManager().getJigsaw();
}
}

View File

@@ -79,6 +79,9 @@ public class IrisJigsawPieceConnector {
@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() + ")";
}

View File

@@ -56,6 +56,10 @@ public class IrisJigsawStructure extends IrisRegistrant {
@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;

View File

@@ -18,20 +18,23 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.annotations.ArrayType;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.MinNumber;
import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.engine.object.annotations.Required;
import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.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
@@ -39,16 +42,33 @@ import lombok.experimental.Accessors;
@Desc("Represents a jigsaw structure placer")
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisJigsawStructurePlacement {
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")
@Desc("The 1 in X chance rarity applies when generating multiple structures at once")
private int rarity = 100;
@Required
@Desc("The salt to use when generating the structure (to differentiate structures)")
private int salt = 76134;
@Required
@MinNumber(0)
@Desc("Average distance in chunks between two neighboring generation attempts")
private int spacing = 32;
@Required
@MinNumber(0)
@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 = 16;
@Desc("The method used to spread the structure")
private SpreadType spreadType = SpreadType.TRIANGULAR;
@ArrayType(type = IrisJigsawMinDistance.class)
@Desc("List of minimum distances to check for")
private KList<IrisJigsawMinDistance> minDistances = new KList<>();
@@ -64,4 +84,42 @@ public class IrisJigsawStructurePlacement {
private int toChunks(int blocks) {
return (int) Math.ceil(blocks / 16d);
}
@ChunkCoordinates
public boolean shouldPlace(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);
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

@@ -506,7 +506,7 @@ public class IrisObject extends IrisRegistrant {
if (rdata != null) {
// Slope condition
if (!config.getSlopeCondition().isDefault() &&
!config.getSlopeCondition().isValid(rdata.getEngine().getComplex().getSlopeStream().get(x, z))) {
!config.getSlopeCondition().isValid(rdata.getEngine().getComplex().getSlopeStream().get(x, z)) && !config.isForcePlace()) {
return -1;
}
@@ -571,8 +571,10 @@ public class IrisObject extends IrisRegistrant {
if (yv < 0) {
if (config.getMode().equals(ObjectPlaceMode.CENTER_HEIGHT) || config.getMode() == ObjectPlaceMode.CENTER_STILT) {
y = (c != null ? c.getSurface() : placer.getHighest(x, z, getLoader(), config.isUnderwater())) + rty;
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
bail = true;
if (!config.isForcePlace()) {
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
bail = true;
}
}
} else if (config.getMode().equals(ObjectPlaceMode.MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.STILT)) {
BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ());
@@ -586,9 +588,11 @@ public class IrisObject extends IrisRegistrant {
for (int i = minX; i <= maxX; i++) {
for (int ii = minZ; ii <= maxZ; ii++) {
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
if (!config.isForcePlace()) {
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
}
}
if (h > y)
y = h;
@@ -610,9 +614,11 @@ public class IrisObject extends IrisRegistrant {
for (int i = minX; i <= maxX; i += Math.abs(xRadius) + 1) {
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
if (!config.isForcePlace()) {
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
}
}
if (h > y)
y = h;
@@ -632,9 +638,11 @@ public class IrisObject extends IrisRegistrant {
for (int i = minX; i <= maxX; i++) {
for (int ii = minZ; ii <= maxZ; ii++) {
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
if (!config.isForcePlace()) {
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
}
}
if (h < y) {
y = h;
@@ -658,9 +666,11 @@ public class IrisObject extends IrisRegistrant {
for (int i = minX; i <= maxX; i += Math.abs(xRadius) + 1) {
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
if (!config.isForcePlace()) {
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
bail = true;
break;
}
}
if (h < y) {
y = h;
@@ -669,45 +679,51 @@ public class IrisObject extends IrisRegistrant {
}
} else if (config.getMode().equals(ObjectPlaceMode.PAINT)) {
y = placer.getHighest(x, z, getLoader(), config.isUnderwater()) + rty;
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
bail = true;
if (!config.isForcePlace()) {
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
bail = true;
}
}
}
} else {
y = yv;
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
bail = true;
if (!config.isForcePlace()) {
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
bail = true;
}
}
}
if (yv >= 0 && config.isBottom()) {
y += Math.floorDiv(h, 2);
bail = placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z);
if (!config.isForcePlace()) {
bail = placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z);
}
}
if (bail) {
if (bail && !config.isForcePlace()) {
return -1;
}
if (yv < 0) {
if (!config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) {
if (!config.isForcePlace() && !config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) {
return -1;
}
}
if (c != null && Math.max(0, h + yrand + ty) + 1 >= c.getHeight()) {
if (!config.isForcePlace() && c != null && Math.max(0, h + yrand + ty) + 1 >= c.getHeight()) {
return -1;
}
if (config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) {
if (!config.isForcePlace() && config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) {
return -1;
}
if (!config.getClamp().canPlace(y + rty + ty, y - rty + ty)) {
if (!config.isForcePlace() && !config.getClamp().canPlace(y + rty + ty, y - rty + ty)) {
return -1;
}
if (!config.getAllowedCollisions().isEmpty() || !config.getForbiddenCollisions().isEmpty()) {
if (!config.isForcePlace() && (!config.getAllowedCollisions().isEmpty() || !config.getForbiddenCollisions().isEmpty())) {
Engine engine = rdata.getEngine();
BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ());
for (int i = x - Math.floorDiv(w, 2) + (int) offset.getX(); i <= x + Math.floorDiv(w, 2) - (w % 2 == 0 ? 1 : 0) + (int) offset.getX(); i++) {

View File

@@ -136,6 +136,8 @@ public class IrisObjectPlacement {
@ArrayType(type = String.class)
@Desc("List of objects to this object is forbidden to collied with")
private KList<String> forbiddenCollisions = new KList<>();
@Desc("Ignore any placement restrictions for this object")
private boolean forcePlace = false;
private transient AtomicCache<TableCache> cache = new AtomicCache<>();
public IrisObjectPlacement toPlacement(String... place) {

View File

@@ -101,6 +101,11 @@ public class IrisObjectRotation {
i.setPosition(rotate(i.getPosition()));
i.setDirection(rotate(i.getDirection()));
}
try {
var translate = piece.getPlacementOptions().getTranslate();
var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ()));
translate.setX(pos.getX()).setY(pos.getY()).setZ(pos.getZ());
} catch (NullPointerException ignored) {}
return piece;
}

View File

@@ -16,7 +16,7 @@ libraries:
- commons-io:commons-io:2.13.0
- io.timeandspace:smoothie-map:2.0.2
- com.google.guava:guava:31.0.1-jre
- com.google.code.gson:gson:2.8.9
- com.google.code.gson:gson:2.10.1
- org.zeroturnaround:zt-zip:1.14
- it.unimi.dsi:fastutil:8.5.6
- org.ow2.asm:asm:9.2