From 61c0ddb15bb90e597a2a18e2bb66f51b73a0e088 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Tue, 16 Apr 2024 13:29:06 +0200 Subject: [PATCH 01/26] fix random jigsaw rotation and offset --- .../iris/engine/jigsaw/PlannedStructure.java | 15 +++++---------- .../iris/engine/object/IrisJigsawPiece.java | 15 ++------------- .../com/volmit/iris/engine/object/IrisObject.java | 12 +++++++++--- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index dd7eade40..4510af468 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -23,7 +23,6 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.mantle.MantleWriter; import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.mantle.Mantle; @@ -32,8 +31,6 @@ import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; import lombok.Data; import org.bukkit.Axis; import org.bukkit.World; -import org.bukkit.block.TileState; -import org.bukkit.block.data.BlockData; @Data public class PlannedStructure { @@ -76,7 +73,7 @@ public class PlannedStructure { public void place(IObjectPlacer placer, Mantle e, Engine eng) { IrisObjectPlacement options = new IrisObjectPlacement(); - options.getRotation().setEnabled(false); + options.setRotation(IrisObjectRotation.of(0,0,0)); int startHeight = pieces.get(0).getPosition().getY(); for (PlannedPiece i : pieces) { @@ -90,11 +87,11 @@ public class PlannedStructure { if (i.getPiece().getPlacementOptions() != null) { options = i.getPiece().getPlacementOptions(); options.getRotation().setEnabled(false); + options.setRotateTowardsSlope(false); } else { options.setMode(i.getPiece().getPlaceMode()); } - IrisObject vo = i.getOgObject(); IrisObject v = i.getObject(); int sx = (v.getW() / 2); int sz = (v.getD() / 2); @@ -122,7 +119,7 @@ public class PlannedStructure { int id = rng.i(0, Integer.MAX_VALUE); JigsawPieceContainer container = JigsawPieceContainer.toContainer(i.getPiece()); - vo.place(xx, height, zz, placer, options, rng, (b, data) -> { + 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()); @@ -167,9 +164,7 @@ public class PlannedStructure { private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea) { if (!piece.getPiece().getPlacementOptions().getRotation().isEnabled()) { - if (generateRotatedPiece(piece, pieceConnector, idea, 0, 0, 0)) { - return true; - } + return generateRotatedPiece(piece, pieceConnector, idea, 0, 0, 0); } KList forder1 = new KList().qadd(0).qadd(1).qadd(2).qadd(3).shuffle(rng); @@ -216,7 +211,7 @@ public class PlannedStructure { } private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea, int x, int y, int z) { - return generateRotatedPiece(piece, pieceConnector, idea, IrisObjectRotation.of(x, y, 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) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java index 72feb927c..93c9f07bc 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java @@ -101,19 +101,8 @@ public class IrisJigsawPiece extends IrisRegistrant { } public IrisJigsawPiece copy() { - IrisJigsawPiece p = new IrisJigsawPiece(); - p.setObject(getObject()); - p.setLoader(getLoader()); - p.setLoadKey(getLoadKey()); - p.setLoadFile(getLoadFile()); - p.setConnectors(new KList<>()); - p.setPlacementOptions(getPlacementOptions()); - - for (IrisJigsawPieceConnector i : getConnectors()) { - p.getConnectors().add(i.copy()); - } - - return p; + var gson = getLoader().getGson(); + return gson.fromJson(gson.toJson(this), IrisJigsawPiece.class); } public boolean isTerminal() { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index 5225c4b23..c4306c115 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -533,10 +533,16 @@ public class IrisObject extends IrisRegistrant { } else if (min == hWest) { slopeRotationY = 270; } + + double newRotation = config.getRotation().getYAxis().getMin() + slopeRotationY; + if (newRotation == 0) { + config.getRotation().setYAxis(new IrisAxisRotationClamp(false, false, 0, 0, 90)); + config.getRotation().setEnabled(config.getRotation().canRotateX() || config.getRotation().canRotateZ()); + } else { + config.getRotation().setYAxis(new IrisAxisRotationClamp(true, false, newRotation, newRotation, 90)); + config.getRotation().setEnabled(true); + } } - double newRotation = config.getRotation().getYAxis().getMin() + slopeRotationY; - config.getRotation().setYAxis(new IrisAxisRotationClamp(true, false, newRotation, newRotation, 90)); - config.getRotation().setEnabled(true); } if (config.isSmartBore()) { From 856c926cde27cbe04a11a2722b0f0a57565610fe Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 17 Apr 2024 21:15:15 +0200 Subject: [PATCH 02/26] add min distance to jigsaw structures --- .../iris/engine/jigsaw/PlannedStructure.java | 23 +++++-- .../components/MantleJigsawComponent.java | 58 ++++++++++++++--- .../engine/object/IrisJigsawDistance.java | 31 ++++++++++ .../object/IrisJigsawStructurePlacement.java | 19 ++++++ .../matter/slices/JigsawStructuresMatter.java | 35 +++++++++++ .../container/JigsawStructuresContainer.java | 62 +++++++++++++++++++ 6 files changed, 214 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java create mode 100644 core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java create mode 100644 core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index 4510af468..ab402cf4c 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -26,8 +26,10 @@ import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; +import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import lombok.Data; import org.bukkit.Axis; import org.bukkit.World; @@ -71,17 +73,28 @@ public class PlannedStructure { } } - public void place(IObjectPlacer placer, Mantle e, Engine eng) { + public boolean place(IObjectPlacer placer, Mantle 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) { - place(i, startHeight, options, placer, e, eng); + 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, chunkPos); + e.set(regionPos.getX(), 0, regionPos.getZ(), slice); + } + return placed; } - public void place(PlannedPiece i, int startHeight, IrisObjectPlacement o, IObjectPlacer placer, Mantle e, Engine eng) { + public boolean place(PlannedPiece i, int startHeight, IrisObjectPlacement o, IObjectPlacer placer, Mantle e, Engine eng) { IrisObjectPlacement options = o; if (i.getPiece().getPlacementOptions() != null) { @@ -119,10 +132,10 @@ public class PlannedStructure { int id = rng.i(0, Integer.MAX_VALUE); JigsawPieceContainer container = JigsawPieceContainer.toContainer(i.getPiece()); - v.place(xx, height, zz, placer, options, rng, (b, data) -> { + 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()); + }, null, getData()) != -1; } public void place(World world) { diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index d9255ef12..14ebeb45d 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -24,12 +24,15 @@ import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.MantleWriter; import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.mantle.MantleFlag; 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 java.util.List; @@ -68,21 +71,58 @@ public class MantleJigsawComponent extends IrisMantleComponent { } } - boolean placed = placeStructures(writer, rng, x, z, biome.getJigsawStructures()); + KSet cachedRegions = new KSet<>(); + KMap> cache = new KMap<>(); + KMap distanceCache = new KMap<>(); + boolean placed = placeStructures(writer, rng, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache); if (!placed) - placed = placeStructures(writer, rng, x, z, region.getJigsawStructures()); + placed = placeStructures(writer, rng, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache); if (!placed) - placeStructures(writer, rng, x, z, getDimension().getJigsawStructures()); + placeStructures(writer, rng, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache); } @ChunkCoordinates - private boolean placeStructures(MantleWriter writer, RNG rng, int x, int z, KList structures) { + private boolean placeStructures(MantleWriter writer, RNG rng, int x, int z, KList structures, + KSet cachedRegions, KMap> cache, KMap distanceCache) { for (IrisJigsawStructurePlacement i : structures) { if (rng.nextInt(i.getRarity()) == 0) { + if (checkDistances(i.collectDistances(), 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()); - place(writer, position, structure, rng); - return true; + if (place(writer, position, structure, rng)) + return true; + } + } + return false; + } + + @ChunkCoordinates + private boolean checkDistances(KMap distances, int x, int z, KSet cachedRegions, KMap> cache, KMap distanceCache) { + int range = 0; + for (int d : distances.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()) { + cache.computeIfAbsent(key, k -> new KSet<>()).addAll(container.getPositions(key)); + } + } + } + Position2 pos = new Position2(x, z); + for (String structure : distances.keySet()) { + if (!cache.containsKey(structure)) continue; + double maxDist = distances.get(structure); + maxDist = maxDist * maxDist; + for (Position2 sPos : cache.get(structure)) { + double dist = distanceCache.computeIfAbsent(sPos, position2 -> position2.distance(pos)); + if (dist > maxDist) return true; } } return false; @@ -91,7 +131,7 @@ public class MantleJigsawComponent extends IrisMantleComponent { @ChunkCoordinates public IrisJigsawStructure guess(int x, int z) { // todo The guess doesnt bring into account that the placer may return -1 - boolean t = false; + // todo doesnt bring skipped placements into account RNG rng = new RNG(cng.fit(-Integer.MAX_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); @@ -130,7 +170,7 @@ public class MantleJigsawComponent extends IrisMantleComponent { } @BlockCoordinates - private void place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) { - new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine()); + private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) { + return new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine()); } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java new file mode 100644 index 000000000..17cbda4c2 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java @@ -0,0 +1,31 @@ +package com.volmit.iris.engine.object; + +import com.volmit.iris.engine.object.annotations.Desc; +import com.volmit.iris.engine.object.annotations.MaxNumber; +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 lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Snippet("jigsaw-structure-distance") +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Desc("Represents the min distance between jigsaw structure placements") +@Data +public class IrisJigsawDistance { + @Required + @RegistryListResource(IrisJigsawStructure.class) + @Desc("The structure to check against") + private String structure; + + @Required + @MinNumber(0) + @MaxNumber(5000) + @Desc("The min distance in blocks to a placed structure\nWARNING: The performance impact scales exponentially!") + private int distance; +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java index 609658e2d..2bb6faa70 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java @@ -18,10 +18,13 @@ package com.volmit.iris.engine.object; +import com.volmit.iris.engine.object.annotations.ArrayType; import com.volmit.iris.engine.object.annotations.Desc; 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 lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -45,4 +48,20 @@ public class IrisJigsawStructurePlacement { @Required @Desc("The 1 in X chance rarity") private int rarity = 100; + + @ArrayType(type = IrisJigsawDistance.class) + @Desc("List of distances to check for") + private KList distances = new KList<>(); + + public KMap collectDistances() { + KMap map = new KMap<>(); + for (IrisJigsawDistance d : distances) { + 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); + } } diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java b/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java new file mode 100644 index 000000000..55803b946 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java @@ -0,0 +1,35 @@ +package com.volmit.iris.util.matter.slices; + +import com.volmit.iris.util.data.palette.Palette; +import com.volmit.iris.util.matter.Sliced; +import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +@Sliced +public class JigsawStructuresMatter extends RawMatter { + public JigsawStructuresMatter() { + this(1, 1, 1); + } + + public JigsawStructuresMatter(int width, int height, int depth) { + super(width, height, depth, JigsawStructuresContainer.class); + } + + @Override + public Palette getGlobalPalette() { + return null; + } + + @Override + public void writeNode(JigsawStructuresContainer b, DataOutputStream dos) throws IOException { + b.write(dos); + } + + @Override + public JigsawStructuresContainer readNode(DataInputStream din) throws IOException { + return new JigsawStructuresContainer(din); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java b/core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java new file mode 100644 index 000000000..68d0f388f --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java @@ -0,0 +1,62 @@ +package com.volmit.iris.util.matter.slices.container; + +import com.volmit.iris.engine.object.IrisJigsawStructure; +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.Position2; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class JigsawStructuresContainer { + private final Map> map = new KMap<>(); + + public JigsawStructuresContainer() {} + + public JigsawStructuresContainer(DataInputStream din) throws IOException { + int s0 = din.readInt(); + for (int i = 0; i < s0; i++) { + int s1 = din.readInt(); + KList list = new KList<>(s1); + for (int j = 0; j < s1; j++) { + list.add(new Position2(din.readInt(), din.readInt())); + } + map.put(din.readUTF(), list); + } + } + + public void write(DataOutputStream dos) throws IOException { + dos.writeInt(map.size()); + for (String key : map.keySet()) { + List list = map.get(key); + dos.writeInt(list.size()); + for (Position2 pos : list) { + dos.writeInt(pos.getX()); + dos.writeInt(pos.getZ()); + } + dos.writeUTF(key); + } + } + + @Unmodifiable + public Set getStructures() { + return Collections.unmodifiableSet(map.keySet()); + } + + @Unmodifiable + public List getPositions(String structure) { + return Collections.unmodifiableList(map.get(structure)); + } + + @ChunkCoordinates + public void add(IrisJigsawStructure structure, Position2 pos) { + map.computeIfAbsent(structure.getLoadKey(), k -> new KList<>()).add(pos); + } +} From c1d9cc62cbea71055aae43e54d3eb03fa5a84f2b Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 17 Apr 2024 21:21:18 +0200 Subject: [PATCH 03/26] woops --- .../java/com/volmit/iris/engine/object/IrisJigsawPiece.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java index 93c9f07bc..c69aca0e4 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java @@ -102,7 +102,11 @@ public class IrisJigsawPiece extends IrisRegistrant { public IrisJigsawPiece copy() { var gson = getLoader().getGson(); - return gson.fromJson(gson.toJson(this), IrisJigsawPiece.class); + IrisJigsawPiece copy = gson.fromJson(gson.toJson(this), IrisJigsawPiece.class); + copy.setLoader(getLoader()); + copy.setLoadKey(getLoadKey()); + copy.setLoadFile(getLoadFile()); + return copy; } public boolean isTerminal() { From d7a283c99f5e17d59f7c24edd9b886d53505214d Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Thu, 18 Apr 2024 16:00:47 +0200 Subject: [PATCH 04/26] fix underwater jigsaw placement --- .../java/com/volmit/iris/engine/jigsaw/PlannedStructure.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index ab402cf4c..fe727eebb 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -118,7 +118,7 @@ public class PlannedStructure { if (i.getStructure().getStructure().getOverrideYRange() != null) { height = (int) i.getStructure().getStructure().getOverrideYRange().get(rng, xx, zz, getData()); } else { - height = placer.getHighest(xx, zz, getData()); + height = placer.getHighest(xx, zz, getData(), options.isUnderwater()); } } else { height = i.getStructure().getStructure().getLockY(); From a7118aa785b3c1cf84eab13016ccfecfe59fa1b3 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 19 Apr 2024 15:18:11 +0200 Subject: [PATCH 05/26] fix jigsaw place command --- .../iris/core/commands/CommandJigsaw.java | 2 +- .../volmit/iris/engine/jigsaw/PlannedPiece.java | 17 +++++++---------- .../iris/engine/jigsaw/PlannedStructure.java | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java index 922041241..a59aa2a69 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java @@ -57,7 +57,7 @@ public class CommandJigsaw implements DecreeExecutor { PrecisionStopwatch p = PrecisionStopwatch.start(); PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation()), new RNG()); sender().sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2)); - ps.place(world()); + ps.place(world(), failed -> sender().sendMessage(failed == 0 ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place " + failed + " pieces!")); } @Decree(description = "Create a jigsaw piece") diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index e93fe5c7d..d2b0d43ea 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -165,7 +165,7 @@ public class PlannedPiece { return connected.size() >= piece.getConnectors().size() || isDead(); } - public void place(World world) { + public boolean place(World world) { PlatformChunkGenerator a = IrisToolbelt.access(world); int minY = 0; @@ -175,14 +175,16 @@ public class PlannedPiece { if (!a.getEngine().getDimension().isBedrock()) minY--; //If the dimension has no bedrock, allow it to go a block lower } + Engine engine = a != null ? a.getEngine() : IrisContext.get().getEngine(); getPiece().getPlacementOptions().setTranslate(new IrisObjectTranslate()); - getPiece().getPlacementOptions().setRotation(rotation); + getPiece().getPlacementOptions().getRotation().setEnabled(false); + getPiece().getPlacementOptions().setRotateTowardsSlope(false); int finalMinY = minY; RNG rng = getStructure().getRng().nextParallelRNG(37555); // TODO: REAL CLASSES!!!!!!! - getOgObject().place(position.getX() + getObject().getCenter().getBlockX(), position.getY() + getObject().getCenter().getBlockY(), position.getZ() + getObject().getCenter().getBlockZ(), new IObjectPlacer() { + 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(); @@ -207,7 +209,6 @@ public class PlannedPiece { IrisLootTable table = getPiece().getPlacementOptions().getTable(block.getBlockData(), getData()); if (table == null) return; - Engine engine = a.getEngine(); engine.addItems(false, ((InventoryHolder) block.getState()).getInventory(), rng.nextParallelRNG(BlockPosition.toLong(x, y, z)), new KList<>(table), InventorySlotType.STORAGE, x, y, z, 15); @@ -258,12 +259,8 @@ public class PlannedPiece { @Override public Engine getEngine() { - if (IrisToolbelt.isIrisWorld(world)) { - return IrisToolbelt.access(world).getEngine(); - } - - return IrisContext.get().getEngine(); + return engine; } - }, piece.getPlacementOptions(), rng, getData()); + }, piece.getPlacementOptions(), rng, getData().getEngine() == null ? engine.getData() : getData()) != -1; } } diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index fe727eebb..36cdcaa00 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -34,6 +34,9 @@ import lombok.Data; import org.bukkit.Axis; import org.bukkit.World; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.IntConsumer; + @Data public class PlannedStructure { private static ConcurrentLinkedHashMap objectRotationCache @@ -138,9 +141,16 @@ public class PlannedStructure { }, null, getData()) != -1; } - public void place(World world) { + public void place(World world, IntConsumer consumer) { + AtomicInteger processed = new AtomicInteger(); + AtomicInteger failures = new AtomicInteger(); for (PlannedPiece i : pieces) { - Iris.sq(() -> i.place(world)); + Iris.sq(() -> { + if (!i.place(world)) failures.incrementAndGet(); + if (processed.incrementAndGet() == pieces.size()) { + consumer.accept(failures.get()); + } + }); } } From 79a4ebcf65c08d65fd05c1ccd84f4b3340fec85d Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 19 Apr 2024 15:29:28 +0200 Subject: [PATCH 06/26] woops --- .../engine/mantle/components/MantleJigsawComponent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index 14ebeb45d..2236723fc 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -118,11 +118,11 @@ public class MantleJigsawComponent extends IrisMantleComponent { Position2 pos = new Position2(x, z); for (String structure : distances.keySet()) { if (!cache.containsKey(structure)) continue; - double maxDist = distances.get(structure); - maxDist = maxDist * maxDist; + double minDist = distances.get(structure); + minDist = minDist * minDist; for (Position2 sPos : cache.get(structure)) { double dist = distanceCache.computeIfAbsent(sPos, position2 -> position2.distance(pos)); - if (dist > maxDist) return true; + if (minDist > dist) return true; } } return false; From 0e666a4c35786ca7c87f28e746c4a0a895d06f4d Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Mon, 22 Apr 2024 14:23:45 +0200 Subject: [PATCH 07/26] change rng noise for jigsaw component --- .../mantle/components/MantleJigsawComponent.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index 2236723fc..69f815fb1 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -18,6 +18,7 @@ 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; @@ -34,20 +35,25 @@ 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 java.util.List; public class MantleJigsawComponent extends IrisMantleComponent { - private final CNG cng; public MantleJigsawComponent(EngineMantle engineMantle) { super(engineMantle, MantleFlag.JIGSAW); - cng = NoiseStyle.STATIC.create(new RNG(engineMantle.getEngine().getSeedManager().getJigsaw())); + } + + 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))); } @Override public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) { - RNG rng = new RNG(cng.fit(-Integer.MAX_VALUE, Integer.MAX_VALUE, x, z)); + RNG rng = applyNoise(x, z); int xxx = 8 + (x << 4); int zzz = 8 + (z << 4); IrisRegion region = getComplex().getRegionStream().get(xxx, zzz); @@ -132,7 +138,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 = new RNG(cng.fit(-Integer.MAX_VALUE, Integer.MAX_VALUE, x, z)); + RNG rng = applyNoise(x, z); IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8); IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8); From cb9a73c60ed397c1414f4444f146f1683d113c2f Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Tue, 23 Apr 2024 10:28:04 +0200 Subject: [PATCH 08/26] rename IrisJigsawDistance to IrisJigsawMinDistance to avoid confusion --- .../mantle/components/MantleJigsawComponent.java | 10 +++++----- ...sJigsawDistance.java => IrisJigsawMinDistance.java} | 6 ++---- .../engine/object/IrisJigsawStructurePlacement.java | 10 +++++----- 3 files changed, 12 insertions(+), 14 deletions(-) rename core/src/main/java/com/volmit/iris/engine/object/{IrisJigsawDistance.java => IrisJigsawMinDistance.java} (85%) diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index 69f815fb1..f10ff67c7 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -92,7 +92,7 @@ public class MantleJigsawComponent extends IrisMantleComponent { KSet cachedRegions, KMap> cache, KMap distanceCache) { for (IrisJigsawStructurePlacement i : structures) { if (rng.nextInt(i.getRarity()) == 0) { - if (checkDistances(i.collectDistances(), x, z, cachedRegions, cache, distanceCache)) + 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()); @@ -104,9 +104,9 @@ public class MantleJigsawComponent extends IrisMantleComponent { } @ChunkCoordinates - private boolean checkDistances(KMap distances, int x, int z, KSet cachedRegions, KMap> cache, KMap distanceCache) { + private boolean checkMinDistances(KMap minDistances, int x, int z, KSet cachedRegions, KMap> cache, KMap distanceCache) { int range = 0; - for (int d : distances.values()) + for (int d : minDistances.values()) range = Math.max(range, d); for (int xx = -range; xx <= range; xx++) { @@ -122,9 +122,9 @@ public class MantleJigsawComponent extends IrisMantleComponent { } } Position2 pos = new Position2(x, z); - for (String structure : distances.keySet()) { + for (String structure : minDistances.keySet()) { if (!cache.containsKey(structure)) continue; - double minDist = distances.get(structure); + double minDist = minDistances.get(structure); minDist = minDist * minDist; for (Position2 sPos : cache.get(structure)) { double dist = distanceCache.computeIfAbsent(sPos, position2 -> position2.distance(pos)); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawMinDistance.java similarity index 85% rename from core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java rename to core/src/main/java/com/volmit/iris/engine/object/IrisJigsawMinDistance.java index 17cbda4c2..818f4b3dd 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawDistance.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawMinDistance.java @@ -1,7 +1,6 @@ package com.volmit.iris.engine.object; import com.volmit.iris.engine.object.annotations.Desc; -import com.volmit.iris.engine.object.annotations.MaxNumber; import com.volmit.iris.engine.object.annotations.MinNumber; import com.volmit.iris.engine.object.annotations.RegistryListResource; import com.volmit.iris.engine.object.annotations.Required; @@ -11,13 +10,13 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -@Snippet("jigsaw-structure-distance") +@Snippet("jigsaw-structure-min-distance") @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor @Desc("Represents the min distance between jigsaw structure placements") @Data -public class IrisJigsawDistance { +public class IrisJigsawMinDistance { @Required @RegistryListResource(IrisJigsawStructure.class) @Desc("The structure to check against") @@ -25,7 +24,6 @@ public class IrisJigsawDistance { @Required @MinNumber(0) - @MaxNumber(5000) @Desc("The min distance in blocks to a placed structure\nWARNING: The performance impact scales exponentially!") private int distance; } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java index 2bb6faa70..16eea76e8 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java @@ -49,13 +49,13 @@ public class IrisJigsawStructurePlacement { @Desc("The 1 in X chance rarity") private int rarity = 100; - @ArrayType(type = IrisJigsawDistance.class) - @Desc("List of distances to check for") - private KList distances = new KList<>(); + @ArrayType(type = IrisJigsawMinDistance.class) + @Desc("List of minimum distances to check for") + private KList minDistances = new KList<>(); - public KMap collectDistances() { + public KMap collectMinDistances() { KMap map = new KMap<>(); - for (IrisJigsawDistance d : distances) { + for (IrisJigsawMinDistance d : minDistances) { map.compute(d.getStructure(), (k, v) -> v != null ? Math.min(toChunks(d.getDistance()), v) : toChunks(d.getDistance())); } return map; From 100e45051408cfa290d477aca343462a9a748acc Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Tue, 23 Apr 2024 13:48:47 +0200 Subject: [PATCH 09/26] fix jigsaw piece collision --- .../java/com/volmit/iris/engine/jigsaw/PlannedPiece.java | 3 +++ .../java/com/volmit/iris/engine/jigsaw/PlannedStructure.java | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index d2b0d43ea..08088ac38 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -30,6 +30,7 @@ import com.volmit.iris.util.math.AxisAlignedBB; import com.volmit.iris.util.math.BlockPosition; import com.volmit.iris.util.math.RNG; import lombok.Data; +import lombok.EqualsAndHashCode; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; @@ -50,10 +51,12 @@ public class PlannedPiece { private IrisObject ogObject; private IrisJigsawPiece piece; private IrisObjectRotation rotation; + @EqualsAndHashCode.Exclude private IrisData data; private KList connected; private boolean dead = false; private AxisAlignedBB box; + @EqualsAndHashCode.Exclude private PlannedStructure structure; public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece) { diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index 36cdcaa00..dc2bf6b68 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -31,6 +31,7 @@ import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import lombok.Data; +import lombok.EqualsAndHashCode; import org.bukkit.Axis; import org.bukkit.World; @@ -319,6 +320,10 @@ public class PlannedStructure { public boolean collidesWith(PlannedPiece piece, PlannedPiece ignore) { for (PlannedPiece i : pieces) { + if (i.equals(ignore)) { + continue; + } + if (i.collidesWith(piece)) { return true; } From c1d5ba55cd4ac65bb523dfe42204eefcab9a418c Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Tue, 23 Apr 2024 13:49:10 +0200 Subject: [PATCH 10/26] fix null pointer --- .../main/java/com/volmit/iris/engine/object/IrisObject.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index c4306c115..665b60c77 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -279,6 +279,8 @@ public class IrisObject extends IrisRegistrant { public synchronized IrisObject copy() { IrisObject o = new IrisObject(w, h, d); o.setLoadKey(o.getLoadKey()); + o.setLoader(getLoader()); + o.setLoadFile(getLoadFile()); o.setCenter(getCenter().clone()); for (BlockVector i : getBlocks().keySet()) { @@ -898,6 +900,7 @@ public class IrisObject extends IrisRegistrant { } } } catch (Throwable e) { + e.printStackTrace(); Iris.reportError(e); } readLock.unlock(); From 6470b2f4a9875c9bf1ba65f434054fd3d439a6a6 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Tue, 23 Apr 2024 13:49:30 +0200 Subject: [PATCH 11/26] fix another null pointer --- .../java/com/volmit/iris/core/commands/CommandJigsaw.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java index a59aa2a69..edab02cdb 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java @@ -34,6 +34,7 @@ import com.volmit.iris.util.decree.specialhandlers.ObjectHandler; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import java.io.File; @@ -56,8 +57,9 @@ public class CommandJigsaw implements DecreeExecutor { ) { PrecisionStopwatch p = PrecisionStopwatch.start(); PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation()), new RNG()); - sender().sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2)); - ps.place(world(), failed -> sender().sendMessage(failed == 0 ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place " + failed + " pieces!")); + 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 == 0 ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place " + failed + " pieces!")); } @Decree(description = "Create a jigsaw piece") From 720417b6c8a1ab3397ef723c1be6a261b15566a1 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 1 May 2024 18:39:42 +0200 Subject: [PATCH 12/26] fix shrinkwrap method --- .../volmit/iris/engine/object/IrisObject.java | 2535 +++++++++-------- 1 file changed, 1268 insertions(+), 1267 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index 665b60c77..ea3d115ea 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -1,1267 +1,1268 @@ -/* - * 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 . - */ - -package com.volmit.iris.engine.object; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.loader.IrisRegistrant; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.framework.PlacedObject; -import com.volmit.iris.engine.framework.placer.HeightmapObjectPlacer; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.context.IrisContext; -import com.volmit.iris.util.data.B; -import com.volmit.iris.util.data.IrisBlockData; -import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.interpolation.IrisInterpolation; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.math.AxisAlignedBB; -import com.volmit.iris.util.math.BlockPosition; -import com.volmit.iris.util.math.Position2; -import com.volmit.iris.util.math.RNG; -import com.volmit.iris.util.matter.MatterMarker; -import com.volmit.iris.util.parallel.BurstExecutor; -import com.volmit.iris.util.parallel.MultiBurst; -import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.scheduling.IrisLock; -import com.volmit.iris.util.scheduling.PrecisionStopwatch; -import com.volmit.iris.util.stream.ProceduralStream; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.TileState; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.MultipleFacing; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.block.data.type.Leaves; -import org.bukkit.util.BlockVector; -import org.bukkit.util.Vector; - -import java.io.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; - -@Accessors(chain = true) -@EqualsAndHashCode(callSuper = false) -public class IrisObject extends IrisRegistrant { - protected static final Vector HALF = new Vector(0.5, 0.5, 0.5); - protected static final BlockData AIR = B.get("CAVE_AIR"); - protected static final BlockData VAIR = B.get("VOID_AIR"); - protected static final BlockData VAIR_DEBUG = B.get("COBWEB"); - protected static final BlockData[] SNOW_LAYERS = new BlockData[]{B.get("minecraft:snow[layers=1]"), B.get("minecraft:snow[layers=2]"), B.get("minecraft:snow[layers=3]"), B.get("minecraft:snow[layers=4]"), B.get("minecraft:snow[layers=5]"), B.get("minecraft:snow[layers=6]"), B.get("minecraft:snow[layers=7]"), B.get("minecraft:snow[layers=8]")}; - protected transient final IrisLock readLock = new IrisLock("read-conclock"); - @Getter - @Setter - protected transient volatile boolean smartBored = false; - @Getter - @Setter - protected transient IrisLock lock = new IrisLock("Preloadcache"); - @Setter - protected transient AtomicCache aabb = new AtomicCache<>(); - private KMap blocks; - private KMap> states; - @Getter - @Setter - private int w; - @Getter - @Setter - private int d; - @Getter - @Setter - private int h; - @Getter - @Setter - private transient BlockVector center; - - public IrisObject(int w, int h, int d) { - blocks = new KMap<>(); - states = new KMap<>(); - this.w = w; - this.h = h; - this.d = d; - center = new BlockVector(w / 2, h / 2, d / 2); - } - - public IrisObject() { - this(0, 0, 0); - } - - public static BlockVector getCenterForSize(BlockVector size) { - return new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); - } - - public static AxisAlignedBB getAABBFor(BlockVector size) { - BlockVector center = new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); - return new AxisAlignedBB(new IrisPosition(new BlockVector(0, 0, 0).subtract(center).toBlockVector()), - new IrisPosition(new BlockVector(size.getX() - 1, size.getY() - 1, size.getZ() - 1).subtract(center).toBlockVector())); - } - - @SuppressWarnings({"resource", "RedundantSuppression"}) - public static BlockVector sampleSize(File file) throws IOException { - FileInputStream in = new FileInputStream(file); - DataInputStream din = new DataInputStream(in); - BlockVector bv = new BlockVector(din.readInt(), din.readInt(), din.readInt()); - Iris.later(din::close); - return bv; - } - - private static List blocksBetweenTwoPoints(Vector loc1, Vector loc2) { - List locations = new ArrayList<>(); - int topBlockX = Math.max(loc1.getBlockX(), loc2.getBlockX()); - int bottomBlockX = Math.min(loc1.getBlockX(), loc2.getBlockX()); - int topBlockY = Math.max(loc1.getBlockY(), loc2.getBlockY()); - int bottomBlockY = Math.min(loc1.getBlockY(), loc2.getBlockY()); - int topBlockZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ()); - int bottomBlockZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ()); - - for (int x = bottomBlockX; x <= topBlockX; x++) { - for (int z = bottomBlockZ; z <= topBlockZ; z++) { - for (int y = bottomBlockY; y <= topBlockY; y++) { - locations.add(new BlockVector(x, y, z)); - } - } - } - return locations; - } - - public AxisAlignedBB getAABB() { - return aabb.aquire(() -> getAABBFor(new BlockVector(w, h, d))); - } - - public void ensureSmartBored(boolean debug) { - if (smartBored) { - return; - } - - PrecisionStopwatch p = PrecisionStopwatch.start(); - BlockData vair = debug ? VAIR_DEBUG : VAIR; - lock.lock(); - AtomicInteger applied = new AtomicInteger(); - if (getBlocks().isEmpty()) { - lock.unlock(); - Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it."); - smartBored = true; - return; - } - - BlockVector max = new BlockVector(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); - BlockVector min = new BlockVector(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); - - for (BlockVector i : getBlocks().keySet()) { - max.setX(Math.max(i.getX(), max.getX())); - min.setX(Math.min(i.getX(), min.getX())); - max.setY(Math.max(i.getY(), max.getY())); - min.setY(Math.min(i.getY(), min.getY())); - max.setZ(Math.max(i.getZ(), max.getZ())); - min.setZ(Math.min(i.getZ(), min.getZ())); - } - - BurstExecutor burst = MultiBurst.burst.burst(); - - // Smash X - for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { - int finalRayY = rayY; - burst.queue(() -> { - for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - - for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) { - if (getBlocks().containsKey(new BlockVector(ray, finalRayY, rayZ))) { - start = Math.min(ray, start); - end = Math.max(ray, end); - } - } - - if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { - for (int i = start; i <= end; i++) { - BlockVector v = new BlockVector(i, finalRayY, rayZ); - - if (!B.isAir(getBlocks().get(v))) { - getBlocks().computeIfAbsent(v, (vv) -> vair); - applied.getAndIncrement(); - } - } - } - } - }); - } - - // Smash Y - for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { - int finalRayX = rayX; - burst.queue(() -> { - for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - - for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) { - if (getBlocks().containsKey(new BlockVector(finalRayX, ray, rayZ))) { - start = Math.min(ray, start); - end = Math.max(ray, end); - } - } - - if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { - for (int i = start; i <= end; i++) { - BlockVector v = new BlockVector(finalRayX, i, rayZ); - - if (!B.isAir(getBlocks().get(v))) { - getBlocks().computeIfAbsent(v, (vv) -> vair); - applied.getAndIncrement(); - } - } - } - } - }); - } - - // Smash Z - for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { - int finalRayX = rayX; - burst.queue(() -> { - for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - - for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) { - if (getBlocks().containsKey(new BlockVector(finalRayX, rayY, ray))) { - start = Math.min(ray, start); - end = Math.max(ray, end); - } - } - - if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { - for (int i = start; i <= end; i++) { - BlockVector v = new BlockVector(finalRayX, rayY, i); - - if (!B.isAir(getBlocks().get(v))) { - getBlocks().computeIfAbsent(v, (vv) -> vair); - applied.getAndIncrement(); - } - } - } - } - }); - } - - burst.complete(); - smartBored = true; - lock.unlock(); - Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")"); - } - - public synchronized IrisObject copy() { - IrisObject o = new IrisObject(w, h, d); - o.setLoadKey(o.getLoadKey()); - o.setLoader(getLoader()); - o.setLoadFile(getLoadFile()); - o.setCenter(getCenter().clone()); - - for (BlockVector i : getBlocks().keySet()) { - o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone()); - } - - for (BlockVector i : getStates().keySet()) { - o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone()); - } - - return o; - } - - public void readLegacy(InputStream in) throws IOException { - DataInputStream din = new DataInputStream(in); - this.w = din.readInt(); - this.h = din.readInt(); - this.d = din.readInt(); - center = new BlockVector(w / 2, h / 2, d / 2); - int s = din.readInt(); - - for (int i = 0; i < s; i++) { - getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF())); - } - - try { - int size = din.readInt(); - - for (int i = 0; i < size; i++) { - getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); - } - } catch (Throwable e) { - Iris.reportError(e); - - } - } - - public void read(InputStream in) throws Throwable { - DataInputStream din = new DataInputStream(in); - this.w = din.readInt(); - this.h = din.readInt(); - this.d = din.readInt(); - if (!din.readUTF().equals("Iris V2 IOB;")) { - return; - } - center = new BlockVector(w / 2, h / 2, d / 2); - int s = din.readShort(); - int i; - KList palette = new KList<>(); - - for (i = 0; i < s; i++) { - palette.add(din.readUTF()); - } - - s = din.readInt(); - - for (i = 0; i < s; i++) { - getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort()))); - } - - s = din.readInt(); - - for (i = 0; i < s; i++) { - getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); - } - } - - public void write(OutputStream o) throws IOException { - DataOutputStream dos = new DataOutputStream(o); - dos.writeInt(w); - dos.writeInt(h); - dos.writeInt(d); - dos.writeUTF("Iris V2 IOB;"); - KList palette = new KList<>(); - - for (BlockData i : getBlocks().values()) { - palette.addIfMissing(i.getAsString()); - } - - dos.writeShort(palette.size()); - - for (String i : palette) { - dos.writeUTF(i); - } - - dos.writeInt(getBlocks().size()); - - for (BlockVector i : getBlocks().keySet()) { - dos.writeShort(i.getBlockX()); - dos.writeShort(i.getBlockY()); - dos.writeShort(i.getBlockZ()); - dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString())); - } - - dos.writeInt(getStates().size()); - for (BlockVector i : getStates().keySet()) { - dos.writeShort(i.getBlockX()); - dos.writeShort(i.getBlockY()); - dos.writeShort(i.getBlockZ()); - getStates().get(i).toBinary(dos); - } - } - - public void read(File file) throws IOException { - FileInputStream fin = new FileInputStream(file); - try { - read(fin); - fin.close(); - } catch (Throwable e) { - Iris.reportError(e); - fin.close(); - fin = new FileInputStream(file); - readLegacy(fin); - fin.close(); - } - } - - public void write(File file) throws IOException { - if (file == null) { - return; - } - - FileOutputStream out = new FileOutputStream(file); - write(out); - out.close(); - } - - public void shrinkwrap() { - BlockVector min = new BlockVector(); - BlockVector max = new BlockVector(); - - for (BlockVector i : getBlocks().keySet()) { - min.setX(Math.min(min.getX(), i.getX())); - min.setY(Math.min(min.getY(), i.getY())); - min.setZ(Math.min(min.getZ(), i.getZ())); - max.setX(Math.max(max.getX(), i.getX())); - max.setY(Math.max(max.getY(), i.getY())); - max.setZ(Math.max(max.getZ(), i.getZ())); - } - - w = max.getBlockX() - min.getBlockX(); - h = max.getBlockY() - min.getBlockY(); - d = max.getBlockZ() - min.getBlockZ(); - } - - public void clean() { - KMap d = new KMap<>(); - - for (BlockVector i : getBlocks().keySet()) { - d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); - } - - KMap> dx = new KMap<>(); - - for (BlockVector i : getBlocks().keySet()) { - d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); - } - - for (BlockVector i : getStates().keySet()) { - dx.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getStates().get(i))); - } - - blocks = d; - states = dx; - } - - public BlockVector getSigned(int x, int y, int z) { - if (x >= w || y >= h || z >= d) { - throw new RuntimeException(x + " " + y + " " + z + " exceeds limit of " + w + " " + h + " " + d); - } - - return new BlockVector(x, y, z).subtract(center).toBlockVector(); - } - - public void setUnsigned(int x, int y, int z, BlockData block) { - BlockVector v = getSigned(x, y, z); - - if (block == null) { - getBlocks().remove(v); - getStates().remove(v); - } else { - getBlocks().put(v, block); - } - } - - public void setUnsigned(int x, int y, int z, Block block) { - BlockVector v = getSigned(x, y, z); - - if (block == null) { - getBlocks().remove(v); - getStates().remove(v); - } else { - BlockData data = block.getBlockData(); - getBlocks().put(v, data); - TileData state = TileData.getTileState(block); - if (state != null) { - Iris.info("Saved State " + v); - getStates().put(v, state); - } - } - } - - public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { - return place(x, -1, z, placer, config, rng, rdata); - } - - public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, CarveResult c, IrisData rdata) { - return place(x, -1, z, placer, config, rng, null, c, rdata); - } - - public int place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { - return place(x, yv, z, placer, config, rng, null, null, rdata); - } - - public int place(Location loc, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { - return place(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), placer, config, rng, rdata); - } - - public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { - IObjectPlacer placer = (config.getHeightmap() != null) ? new HeightmapObjectPlacer(oplacer.getEngine() == null ? IrisContext.get().getEngine() : oplacer.getEngine(), rng, x, yv, z, config, oplacer) : oplacer; - - if (rdata != null) { - // Slope condition - if (!config.getSlopeCondition().isDefault() && - !config.getSlopeCondition().isValid(rdata.getEngine().getComplex().getSlopeStream().get(x, z))) { - return -1; - } - -// if (config.getCarvingSupport().supportsSurface()) { -// int y = placer.getHighest(x, z, rdata); -// if (placer.isCarved(x, y, z)) { -// return -1; -// } -// } - - // Rotation calculation - int slopeRotationY = 0; - ProceduralStream heightStream = rdata.getEngine().getComplex().getHeightStream(); - if (config.isRotateTowardsSlope()) { - // Whichever side of the rectangle that bounds the object is lowest is the 'direction' of the slope (simply said). - double hNorth = heightStream.get(x, z + ((float) d) / 2); - double hEast = heightStream.get(x + ((float) w) / 2, z); - double hSouth = heightStream.get(x, z - ((float) d) / 2); - double hWest = heightStream.get(x - ((float) w) / 2, z); - double min = Math.min(Math.min(hNorth, hEast), Math.min(hSouth, hWest)); - if (min == hNorth) { - slopeRotationY = 0; - } else if (min == hEast) { - slopeRotationY = 90; - } else if (min == hSouth) { - slopeRotationY = 180; - } else if (min == hWest) { - slopeRotationY = 270; - } - - double newRotation = config.getRotation().getYAxis().getMin() + slopeRotationY; - if (newRotation == 0) { - config.getRotation().setYAxis(new IrisAxisRotationClamp(false, false, 0, 0, 90)); - config.getRotation().setEnabled(config.getRotation().canRotateX() || config.getRotation().canRotateZ()); - } else { - config.getRotation().setYAxis(new IrisAxisRotationClamp(true, false, newRotation, newRotation, 90)); - config.getRotation().setEnabled(true); - } - } - } - - if (config.isSmartBore()) { - ensureSmartBored(placer.isDebugSmartBore()); - } - - boolean warped = !config.getWarp().isFlat(); - boolean stilting = (config.getMode().equals(ObjectPlaceMode.STILT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT) || - config.getMode() == ObjectPlaceMode.MIN_STILT || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT || - config.getMode() == ObjectPlaceMode.CENTER_STILT); - KMap heightmap = config.getSnow() > 0 ? new KMap<>() : null; - int spinx = rng.imax() / 1000; - int spiny = rng.imax() / 1000; - int spinz = rng.imax() / 1000; - int rty = config.getRotation().rotate(new BlockVector(0, getCenter().getBlockY(), 0), spinx, spiny, spinz).getBlockY(); - int ty = config.getTranslate().translate(new BlockVector(0, getCenter().getBlockY(), 0), config.getRotation(), spinx, spiny, spinz).getBlockY(); - int y = -1; - int xx, zz; - int yrand = config.getTranslate().getYRandom(); - yrand = yrand > 0 ? rng.i(0, yrand) : yrand < 0 ? rng.i(yrand, 0) : yrand; - boolean bail = false; - - 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; - } - } 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()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - 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 (h > y) - y = h; - } - } - } else if (config.getMode().equals(ObjectPlaceMode.FAST_MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT)) { - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - - int xRadius = (rotatedDimensions.getBlockX() / 2); - int xLength = xRadius + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zRadius = (rotatedDimensions.getBlockZ() / 2); - int zLength = zRadius + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - - 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 (h > y) - y = h; - } - } - } else if (config.getMode().equals(ObjectPlaceMode.MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.MIN_STILT) { - y = rdata.getEngine().getHeight() + 1; - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - - int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - 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 (h < y) { - y = h; - } - } - } - } else if (config.getMode().equals(ObjectPlaceMode.FAST_MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT) { - y = rdata.getEngine().getHeight() + 1; - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - - int xRadius = (rotatedDimensions.getBlockX() / 2); - int xLength = xRadius + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zRadius = (rotatedDimensions.getBlockZ() / 2); - int zLength = zRadius + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - - 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 (h < y) { - y = h; - } - } - } - } 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; - } - } - } 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 (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 (bail) { - return -1; - } - - if (yv < 0) { - if (!config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) { - return -1; - } - } - - if (c != null && Math.max(0, h + yrand + ty) + 1 >= c.getHeight()) { - return -1; - } - - if (config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) { - return -1; - } - - if (!config.getClamp().canPlace(y + rty + ty, y - rty + ty)) { - return -1; - } - - if (!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++) { - for (int j = y - Math.floorDiv(h, 2) + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { - for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { - PlacedObject p = engine.getObjectPlacement(i, j, k); - if (p == null) continue; - IrisObject o = p.getObject(); - if (o == null) continue; - String key = o.getLoadKey(); - if (key != null) { - if (config.getForbiddenCollisions().contains(key) && !config.getAllowedCollisions().contains(key)) { - // Iris.debug("%s collides with %s (%s / %s / %s)", getLoadKey(), key, i, j, k); - return -1; - } - } - } - } - } - } - - if (config.isBore()) { - 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++) { - for (int j = y - Math.floorDiv(h, 2) - config.getBoreExtendMinY() + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) + config.getBoreExtendMaxY() - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { - for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { - placer.set(i, j, k, AIR); - } - } - } - } - - int lowest = Integer.MAX_VALUE; - y += yrand; - readLock.lock(); - - KMap markers = null; - - try { - if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) { - markers = new KMap<>(); - for (IrisObjectMarker j : config.getMarkers()) { - IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker()); - - if (marker == null) { - continue; - } - - int max = j.getMaximumMarkers(); - - for (BlockVector i : getBlocks().k().shuffle()) { - if (max <= 0) { - break; - } - - BlockData data = getBlocks().get(i); - - for (BlockData k : j.getMark(rdata)) { - if (max <= 0) { - break; - } - - if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { - boolean a = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 1, 0)))); - boolean fff = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 2, 0)))); - - if (!marker.isEmptyAbove() || (a && fff)) { - markers.put(i, j.getMarker()); - max--; - } - } - } - } - } - } - - for (BlockVector g : getBlocks().keySet()) { - BlockData d; - TileData tile = null; - - try { - d = getBlocks().get(g); - tile = getStates().get(g); - } catch (Throwable e) { - Iris.reportError(e); - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)"); - d = AIR; - } - - if (d == null) { - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (null)"); - d = AIR; - } - - BlockVector i = g.clone(); - BlockData data = d.clone(); - i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); - i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); - - if (stilting && i.getBlockY() < lowest && !B.isAir(data)) { - lowest = i.getBlockY(); - } - - if (placer.isPreventingDecay() && (data) instanceof Leaves && !((Leaves) (data)).isPersistent()) { - ((Leaves) data).setPersistent(true); - } - - for (IrisObjectReplace j : config.getEdit()) { - if (rng.chance(j.getChance())) { - for (BlockData k : j.getFind(rdata)) { - if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { - BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); - - if (newData.getMaterial() == data.getMaterial() && !(newData instanceof IrisBlockData || data instanceof IrisBlockData)) - data = data.merge(newData); - else - data = newData; - - if (newData.getMaterial() == Material.SPAWNER) { - Optional> t = j.getReplace().getTile(rng, x, y, z, rdata); - if (t.isPresent()) { - tile = t.get(); - } - } - } - } - } - } - - data = config.getRotation().rotate(data, spinx, spiny, spinz); - xx = x + (int) Math.round(i.getX()); - - int yy = y + (int) Math.round(i.getY()); - zz = z + (int) Math.round(i.getZ()); - - if (warped) { - xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); - zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); - } - - if (yv < 0 && (config.getMode().equals(ObjectPlaceMode.PAINT)) && !B.isVineBlock(data)) { - yy = (int) Math.round(i.getY()) + Math.floorDiv(h, 2) + placer.getHighest(xx, zz, getLoader(), config.isUnderwater()); - } - - if (heightmap != null) { - Position2 pos = new Position2(xx, zz); - - if (!heightmap.containsKey(pos)) { - heightmap.put(pos, yy); - } - - if (heightmap.get(pos) < yy) { - heightmap.put(pos, yy); - } - } - - if (config.isMeld() && !placer.isSolid(xx, yy, zz)) { - continue; - } - - if ((config.isWaterloggable() || config.isUnderwater()) && yy <= placer.getFluidHeight() && data instanceof Waterlogged) { - // TODO Here - ((Waterlogged) data).setWaterlogged(true); - } - - if (B.isVineBlock(data)) { - MultipleFacing f = (MultipleFacing) data; - for (BlockFace face : f.getAllowedFaces()) { - BlockData facingBlock = placer.get(xx + face.getModX(), yy + face.getModY(), zz + face.getModZ()); - if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { - f.setFace(face, true); - } - } - } - - if (listener != null) { - listener.accept(new BlockPosition(xx, yy, zz), data); - } - - if (markers != null && markers.containsKey(g)) { - placer.getEngine().getMantle().getMantle().set(xx, yy, zz, new MatterMarker(markers.get(g))); - } - - boolean wouldReplace = B.isSolid(placer.get(xx, yy, zz)) && B.isVineBlock(data); - - if (!data.getMaterial().equals(Material.AIR) && !data.getMaterial().equals(Material.CAVE_AIR) && !wouldReplace) { - placer.set(xx, yy, zz, data); - if (tile != null) { - placer.setTile(xx, yy, zz, tile); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - Iris.reportError(e); - } - readLock.unlock(); - - if (stilting) { - readLock.lock(); - IrisStiltSettings settings = config.getStiltSettings(); - for (BlockVector g : getBlocks().keySet()) { - BlockData d; - - if (settings == null || settings.getPalette() == null) { - try { - d = getBlocks().get(g); - } catch (Throwable e) { - Iris.reportError(e); - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)"); - d = AIR; - } - - if (d == null) { - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt null)"); - d = AIR; - } - } else - d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata); - - - BlockVector i = g.clone(); - i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); - i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); - d = config.getRotation().rotate(d, spinx, spiny, spinz); - - if (i.getBlockY() != lowest) - continue; - - for (IrisObjectReplace j : config.getEdit()) { - if (rng.chance(j.getChance())) { - for (BlockData k : j.getFind(rdata)) { - if (j.isExact() ? k.matches(d) : k.getMaterial().equals(d.getMaterial())) { - BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); - - if (newData.getMaterial() == d.getMaterial()) { - d = d.merge(newData); - } else { - d = newData; - } - } - } - } - } - - if (d == null || B.isAir(d)) - continue; - - xx = x + (int) Math.round(i.getX()); - zz = z + (int) Math.round(i.getZ()); - - if (warped) { - xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); - zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); - } - - int highest = placer.getHighest(xx, zz, getLoader(), true); - - if ((config.isWaterloggable() || config.isUnderwater()) && highest <= placer.getFluidHeight() && d instanceof Waterlogged) - ((Waterlogged) d).setWaterlogged(true); - - if (yv >= 0 && config.isBottom()) - y += Math.floorDiv(h, 2); - - int lowerBound = highest - 1; - if (settings != null) { - lowerBound -= config.getStiltSettings().getOverStilt() - rng.i(0, config.getStiltSettings().getYRand()); - if (settings.getYMax() != 0) - lowerBound -= Math.min(config.getStiltSettings().getYMax() - (lowest + y - highest), 0); - } - for (int j = lowest + y; j > lowerBound; j--) { - if (B.isVineBlock(d)) { - MultipleFacing f = (MultipleFacing) d; - for (BlockFace face : f.getAllowedFaces()) { - BlockData facingBlock = placer.get(xx + face.getModX(), j + face.getModY(), zz + face.getModZ()); - if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { - f.setFace(face, true); - } - } - } - placer.set(xx, j, zz, d); - } - - } - - readLock.unlock(); - } - - if (heightmap != null) { - RNG rngx = rng.nextParallelRNG(3468854); - - for (Position2 i : heightmap.k()) { - int vx = i.getX(); - int vy = heightmap.get(i); - int vz = i.getZ(); - - if (config.getSnow() > 0) { - int height = rngx.i(0, (int) (config.getSnow() * 7)); - placer.set(vx, vy + 1, vz, SNOW_LAYERS[Math.max(Math.min(height, 7), 0)]); - } - } - } - - return y; - } - - public IrisObject rotateCopy(IrisObjectRotation rt) { - IrisObject copy = copy(); - copy.rotate(rt, 0, 0, 0); - return copy; - } - - public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) { - KMap d = new KMap<>(); - - for (BlockVector i : getBlocks().keySet()) { - d.put(r.rotate(i.clone(), spinx, spiny, spinz), r.rotate(getBlocks().get(i).clone(), - spinx, spiny, spinz)); - } - - KMap> dx = new KMap<>(); - - for (BlockVector i : getStates().keySet()) { - dx.put(r.rotate(i.clone(), spinx, spiny, spinz), getStates().get(i)); - } - - blocks = d; - states = dx; - } - - public void place(Location at) { - for (BlockVector i : getBlocks().keySet()) { - Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock(); - b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); - - if (getStates().containsKey(i)) { - Iris.info(Objects.requireNonNull(states.get(i)).toString()); - BlockState st = b.getState(); - Objects.requireNonNull(getStates().get(i)).toBukkitTry(st); - st.update(); - } - } - } - - public void placeCenterY(Location at) { - for (BlockVector i : getBlocks().keySet()) { - Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock(); - b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); - - if (getStates().containsKey(i)) { - Objects.requireNonNull(getStates().get(i)).toBukkitTry(b.getState()); - } - } - } - - public synchronized KMap getBlocks() { - return blocks; - } - - public synchronized KMap> getStates() { - return states; - } - - public void unplaceCenterY(Location at) { - for (BlockVector i : getBlocks().keySet()) { - at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false); - } - } - - public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) { - Vector sm1 = new Vector(scale - 1, scale - 1, scale - 1); - scale = Math.max(0.001, Math.min(50, scale)); - if (scale < 1) { - scale = scale - 0.0001; - } - - IrisPosition l1 = getAABB().max(); - IrisPosition l2 = getAABB().min(); - @SuppressWarnings({"unchecked", "rawtypes"}) HashMap placeBlock = new HashMap(); - - Vector center = getCenter(); - if (getH() == 2) { - center = center.setY(center.getBlockY() + 0.5); - } - if (getW() == 2) { - center = center.setX(center.getBlockX() + 0.5); - } - if (getD() == 2) { - center = center.setZ(center.getBlockZ() + 0.5); - } - - IrisObject oo = new IrisObject((int) Math.ceil((w * scale) + (scale * 2)), (int) Math.ceil((h * scale) + (scale * 2)), (int) Math.ceil((d * scale) + (scale * 2))); - - for (Map.Entry entry : blocks.entrySet()) { - BlockData bd = entry.getValue(); - placeBlock.put(entry.getKey().clone().add(HALF).subtract(center) - .multiply(scale).add(sm1).toBlockVector(), bd); - } - - for (Map.Entry entry : placeBlock.entrySet()) { - BlockVector v = entry.getKey(); - if (scale > 1) { - for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) { - oo.getBlocks().put(vec, entry.getValue()); - } - } else { - oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue()); - } - } - - if (scale > 1) { - switch (interpolation) { - case TRILINEAR -> oo.trilinear((int) Math.round(scale)); - case TRICUBIC -> oo.tricubic((int) Math.round(scale)); - case TRIHERMITE -> oo.trihermite((int) Math.round(scale)); - } - } - - return oo; - } - - public void trilinear(int rad) { - KMap v = getBlocks().copy(); - KMap b = new KMap<>(); - BlockVector min = getAABB().minbv(); - BlockVector max = getAABB().maxbv(); - - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - if (IrisInterpolation.getTrilinear(x, y, z, rad, (xx, yy, zz) -> { - BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); - - if (data == null || data.getMaterial().isAir()) { - return 0; - } - - return 1; - }) >= 0.5) { - b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); - } else { - b.put(new BlockVector(x, y, z), AIR); - } - } - } - } - - blocks = b; - } - - public void tricubic(int rad) { - KMap v = getBlocks().copy(); - KMap b = new KMap<>(); - BlockVector min = getAABB().minbv(); - BlockVector max = getAABB().maxbv(); - - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - if (IrisInterpolation.getTricubic(x, y, z, rad, (xx, yy, zz) -> { - BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); - - if (data == null || data.getMaterial().isAir()) { - return 0; - } - - return 1; - }) >= 0.5) { - b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); - } else { - b.put(new BlockVector(x, y, z), AIR); - } - } - } - } - - blocks = b; - } - - public void trihermite(int rad) { - trihermite(rad, 0D, 0D); - } - - public void trihermite(int rad, double tension, double bias) { - KMap v = getBlocks().copy(); - KMap b = new KMap<>(); - BlockVector min = getAABB().minbv(); - BlockVector max = getAABB().maxbv(); - - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - if (IrisInterpolation.getTrihermite(x, y, z, rad, (xx, yy, zz) -> { - BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); - - if (data == null || data.getMaterial().isAir()) { - return 0; - } - - return 1; - }, tension, bias) >= 0.5) { - b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); - } else { - b.put(new BlockVector(x, y, z), AIR); - } - } - } - } - - blocks = b; - } - - private BlockData nearestBlockData(int x, int y, int z) { - BlockVector vv = new BlockVector(x, y, z); - BlockData r = getBlocks().get(vv); - - if (r != null && !r.getMaterial().isAir()) { - return r; - } - - double d = Double.MAX_VALUE; - - for (Map.Entry entry : blocks.entrySet()) { - BlockData dat = entry.getValue(); - - if (dat.getMaterial().isAir()) { - continue; - } - - double dx = entry.getKey().distanceSquared(vv); - - if (dx < d) { - d = dx; - r = dat; - } - } - - return r; - } - - public int volume() { - return blocks.size(); - } - - @Override - public String getFolderName() { - return "objects"; - } - - @Override - public String getTypeName() { - return "Object"; - } - - @Override - public void scanForErrors(JSONObject p, VolmitSender sender) { - } -} +/* + * 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 . + */ + +package com.volmit.iris.engine.object; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.loader.IrisRegistrant; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.PlacedObject; +import com.volmit.iris.engine.framework.placer.HeightmapObjectPlacer; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.context.IrisContext; +import com.volmit.iris.util.data.B; +import com.volmit.iris.util.data.IrisBlockData; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.interpolation.IrisInterpolation; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.math.AxisAlignedBB; +import com.volmit.iris.util.math.BlockPosition; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.matter.MatterMarker; +import com.volmit.iris.util.parallel.BurstExecutor; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.IrisLock; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import com.volmit.iris.util.stream.ProceduralStream; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.TileState; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.MultipleFacing; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.block.data.type.Leaves; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Vector; + +import java.io.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class IrisObject extends IrisRegistrant { + protected static final Vector HALF = new Vector(0.5, 0.5, 0.5); + protected static final BlockData AIR = B.get("CAVE_AIR"); + protected static final BlockData VAIR = B.get("VOID_AIR"); + protected static final BlockData VAIR_DEBUG = B.get("COBWEB"); + protected static final BlockData[] SNOW_LAYERS = new BlockData[]{B.get("minecraft:snow[layers=1]"), B.get("minecraft:snow[layers=2]"), B.get("minecraft:snow[layers=3]"), B.get("minecraft:snow[layers=4]"), B.get("minecraft:snow[layers=5]"), B.get("minecraft:snow[layers=6]"), B.get("minecraft:snow[layers=7]"), B.get("minecraft:snow[layers=8]")}; + protected transient final IrisLock readLock = new IrisLock("read-conclock"); + @Getter + @Setter + protected transient volatile boolean smartBored = false; + @Getter + @Setter + protected transient IrisLock lock = new IrisLock("Preloadcache"); + @Setter + protected transient AtomicCache aabb = new AtomicCache<>(); + private KMap blocks; + private KMap> states; + @Getter + @Setter + private int w; + @Getter + @Setter + private int d; + @Getter + @Setter + private int h; + @Getter + @Setter + private transient BlockVector center; + + public IrisObject(int w, int h, int d) { + blocks = new KMap<>(); + states = new KMap<>(); + this.w = w; + this.h = h; + this.d = d; + center = new BlockVector(w / 2, h / 2, d / 2); + } + + public IrisObject() { + this(0, 0, 0); + } + + public static BlockVector getCenterForSize(BlockVector size) { + return new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); + } + + public static AxisAlignedBB getAABBFor(BlockVector size) { + BlockVector center = new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); + return new AxisAlignedBB(new IrisPosition(new BlockVector(0, 0, 0).subtract(center).toBlockVector()), + new IrisPosition(new BlockVector(size.getX() - 1, size.getY() - 1, size.getZ() - 1).subtract(center).toBlockVector())); + } + + @SuppressWarnings({"resource", "RedundantSuppression"}) + public static BlockVector sampleSize(File file) throws IOException { + FileInputStream in = new FileInputStream(file); + DataInputStream din = new DataInputStream(in); + BlockVector bv = new BlockVector(din.readInt(), din.readInt(), din.readInt()); + Iris.later(din::close); + return bv; + } + + private static List blocksBetweenTwoPoints(Vector loc1, Vector loc2) { + List locations = new ArrayList<>(); + int topBlockX = Math.max(loc1.getBlockX(), loc2.getBlockX()); + int bottomBlockX = Math.min(loc1.getBlockX(), loc2.getBlockX()); + int topBlockY = Math.max(loc1.getBlockY(), loc2.getBlockY()); + int bottomBlockY = Math.min(loc1.getBlockY(), loc2.getBlockY()); + int topBlockZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ()); + int bottomBlockZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ()); + + for (int x = bottomBlockX; x <= topBlockX; x++) { + for (int z = bottomBlockZ; z <= topBlockZ; z++) { + for (int y = bottomBlockY; y <= topBlockY; y++) { + locations.add(new BlockVector(x, y, z)); + } + } + } + return locations; + } + + public AxisAlignedBB getAABB() { + return aabb.aquire(() -> getAABBFor(new BlockVector(w, h, d))); + } + + public void ensureSmartBored(boolean debug) { + if (smartBored) { + return; + } + + PrecisionStopwatch p = PrecisionStopwatch.start(); + BlockData vair = debug ? VAIR_DEBUG : VAIR; + lock.lock(); + AtomicInteger applied = new AtomicInteger(); + if (getBlocks().isEmpty()) { + lock.unlock(); + Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it."); + smartBored = true; + return; + } + + BlockVector max = new BlockVector(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); + BlockVector min = new BlockVector(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + + for (BlockVector i : getBlocks().keySet()) { + max.setX(Math.max(i.getX(), max.getX())); + min.setX(Math.min(i.getX(), min.getX())); + max.setY(Math.max(i.getY(), max.getY())); + min.setY(Math.min(i.getY(), min.getY())); + max.setZ(Math.max(i.getZ(), max.getZ())); + min.setZ(Math.min(i.getZ(), min.getZ())); + } + + BurstExecutor burst = MultiBurst.burst.burst(); + + // Smash X + for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { + int finalRayY = rayY; + burst.queue(() -> { + for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + + for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) { + if (getBlocks().containsKey(new BlockVector(ray, finalRayY, rayZ))) { + start = Math.min(ray, start); + end = Math.max(ray, end); + } + } + + if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { + for (int i = start; i <= end; i++) { + BlockVector v = new BlockVector(i, finalRayY, rayZ); + + if (!B.isAir(getBlocks().get(v))) { + getBlocks().computeIfAbsent(v, (vv) -> vair); + applied.getAndIncrement(); + } + } + } + } + }); + } + + // Smash Y + for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { + int finalRayX = rayX; + burst.queue(() -> { + for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + + for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) { + if (getBlocks().containsKey(new BlockVector(finalRayX, ray, rayZ))) { + start = Math.min(ray, start); + end = Math.max(ray, end); + } + } + + if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { + for (int i = start; i <= end; i++) { + BlockVector v = new BlockVector(finalRayX, i, rayZ); + + if (!B.isAir(getBlocks().get(v))) { + getBlocks().computeIfAbsent(v, (vv) -> vair); + applied.getAndIncrement(); + } + } + } + } + }); + } + + // Smash Z + for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { + int finalRayX = rayX; + burst.queue(() -> { + for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + + for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) { + if (getBlocks().containsKey(new BlockVector(finalRayX, rayY, ray))) { + start = Math.min(ray, start); + end = Math.max(ray, end); + } + } + + if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { + for (int i = start; i <= end; i++) { + BlockVector v = new BlockVector(finalRayX, rayY, i); + + if (!B.isAir(getBlocks().get(v))) { + getBlocks().computeIfAbsent(v, (vv) -> vair); + applied.getAndIncrement(); + } + } + } + } + }); + } + + burst.complete(); + smartBored = true; + lock.unlock(); + Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")"); + } + + public synchronized IrisObject copy() { + IrisObject o = new IrisObject(w, h, d); + o.setLoadKey(o.getLoadKey()); + o.setLoader(getLoader()); + o.setLoadFile(getLoadFile()); + o.setCenter(getCenter().clone()); + + for (BlockVector i : getBlocks().keySet()) { + o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone()); + } + + for (BlockVector i : getStates().keySet()) { + o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone()); + } + + return o; + } + + public void readLegacy(InputStream in) throws IOException { + DataInputStream din = new DataInputStream(in); + this.w = din.readInt(); + this.h = din.readInt(); + this.d = din.readInt(); + center = new BlockVector(w / 2, h / 2, d / 2); + int s = din.readInt(); + + for (int i = 0; i < s; i++) { + getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF())); + } + + try { + int size = din.readInt(); + + for (int i = 0; i < size; i++) { + getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); + } + } catch (Throwable e) { + Iris.reportError(e); + + } + } + + public void read(InputStream in) throws Throwable { + DataInputStream din = new DataInputStream(in); + this.w = din.readInt(); + this.h = din.readInt(); + this.d = din.readInt(); + if (!din.readUTF().equals("Iris V2 IOB;")) { + return; + } + center = new BlockVector(w / 2, h / 2, d / 2); + int s = din.readShort(); + int i; + KList palette = new KList<>(); + + for (i = 0; i < s; i++) { + palette.add(din.readUTF()); + } + + s = din.readInt(); + + for (i = 0; i < s; i++) { + getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort()))); + } + + s = din.readInt(); + + for (i = 0; i < s; i++) { + getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); + } + } + + public void write(OutputStream o) throws IOException { + DataOutputStream dos = new DataOutputStream(o); + dos.writeInt(w); + dos.writeInt(h); + dos.writeInt(d); + dos.writeUTF("Iris V2 IOB;"); + KList palette = new KList<>(); + + for (BlockData i : getBlocks().values()) { + palette.addIfMissing(i.getAsString()); + } + + dos.writeShort(palette.size()); + + for (String i : palette) { + dos.writeUTF(i); + } + + dos.writeInt(getBlocks().size()); + + for (BlockVector i : getBlocks().keySet()) { + dos.writeShort(i.getBlockX()); + dos.writeShort(i.getBlockY()); + dos.writeShort(i.getBlockZ()); + dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString())); + } + + dos.writeInt(getStates().size()); + for (BlockVector i : getStates().keySet()) { + dos.writeShort(i.getBlockX()); + dos.writeShort(i.getBlockY()); + dos.writeShort(i.getBlockZ()); + getStates().get(i).toBinary(dos); + } + } + + public void read(File file) throws IOException { + FileInputStream fin = new FileInputStream(file); + try { + read(fin); + fin.close(); + } catch (Throwable e) { + Iris.reportError(e); + fin.close(); + fin = new FileInputStream(file); + readLegacy(fin); + fin.close(); + } + } + + public void write(File file) throws IOException { + if (file == null) { + return; + } + + FileOutputStream out = new FileOutputStream(file); + write(out); + out.close(); + } + + public void shrinkwrap() { + BlockVector min = new BlockVector(); + BlockVector max = new BlockVector(); + + for (BlockVector i : getBlocks().keySet()) { + min.setX(Math.min(min.getX(), i.getX())); + min.setY(Math.min(min.getY(), i.getY())); + min.setZ(Math.min(min.getZ(), i.getZ())); + max.setX(Math.max(max.getX(), i.getX())); + max.setY(Math.max(max.getY(), i.getY())); + max.setZ(Math.max(max.getZ(), i.getZ())); + } + + w = max.getBlockX() - min.getBlockX() + (min.getBlockX() <= 0 && max.getBlockX() >= 0 && min.getBlockX() != max.getBlockX() ? 1 : 0); + h = max.getBlockY() - min.getBlockY() + (min.getBlockY() <= 0 && max.getBlockY() >= 0 && min.getBlockY() != max.getBlockY() ? 1 : 0); + d = max.getBlockZ() - min.getBlockZ() + (min.getBlockZ() <= 0 && max.getBlockZ() >= 0 && min.getBlockZ() != max.getBlockZ() ? 1 : 0); + center = new BlockVector(w / 2, h / 2, d / 2); + } + + public void clean() { + KMap d = new KMap<>(); + + for (BlockVector i : getBlocks().keySet()) { + d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); + } + + KMap> dx = new KMap<>(); + + for (BlockVector i : getBlocks().keySet()) { + d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); + } + + for (BlockVector i : getStates().keySet()) { + dx.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getStates().get(i))); + } + + blocks = d; + states = dx; + } + + public BlockVector getSigned(int x, int y, int z) { + if (x >= w || y >= h || z >= d) { + throw new RuntimeException(x + " " + y + " " + z + " exceeds limit of " + w + " " + h + " " + d); + } + + return new BlockVector(x, y, z).subtract(center).toBlockVector(); + } + + public void setUnsigned(int x, int y, int z, BlockData block) { + BlockVector v = getSigned(x, y, z); + + if (block == null) { + getBlocks().remove(v); + getStates().remove(v); + } else { + getBlocks().put(v, block); + } + } + + public void setUnsigned(int x, int y, int z, Block block) { + BlockVector v = getSigned(x, y, z); + + if (block == null) { + getBlocks().remove(v); + getStates().remove(v); + } else { + BlockData data = block.getBlockData(); + getBlocks().put(v, data); + TileData state = TileData.getTileState(block); + if (state != null) { + Iris.info("Saved State " + v); + getStates().put(v, state); + } + } + } + + public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { + return place(x, -1, z, placer, config, rng, rdata); + } + + public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, CarveResult c, IrisData rdata) { + return place(x, -1, z, placer, config, rng, null, c, rdata); + } + + public int place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { + return place(x, yv, z, placer, config, rng, null, null, rdata); + } + + public int place(Location loc, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { + return place(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), placer, config, rng, rdata); + } + + public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { + IObjectPlacer placer = (config.getHeightmap() != null) ? new HeightmapObjectPlacer(oplacer.getEngine() == null ? IrisContext.get().getEngine() : oplacer.getEngine(), rng, x, yv, z, config, oplacer) : oplacer; + + if (rdata != null) { + // Slope condition + if (!config.getSlopeCondition().isDefault() && + !config.getSlopeCondition().isValid(rdata.getEngine().getComplex().getSlopeStream().get(x, z))) { + return -1; + } + +// if (config.getCarvingSupport().supportsSurface()) { +// int y = placer.getHighest(x, z, rdata); +// if (placer.isCarved(x, y, z)) { +// return -1; +// } +// } + + // Rotation calculation + int slopeRotationY = 0; + ProceduralStream heightStream = rdata.getEngine().getComplex().getHeightStream(); + if (config.isRotateTowardsSlope()) { + // Whichever side of the rectangle that bounds the object is lowest is the 'direction' of the slope (simply said). + double hNorth = heightStream.get(x, z + ((float) d) / 2); + double hEast = heightStream.get(x + ((float) w) / 2, z); + double hSouth = heightStream.get(x, z - ((float) d) / 2); + double hWest = heightStream.get(x - ((float) w) / 2, z); + double min = Math.min(Math.min(hNorth, hEast), Math.min(hSouth, hWest)); + if (min == hNorth) { + slopeRotationY = 0; + } else if (min == hEast) { + slopeRotationY = 90; + } else if (min == hSouth) { + slopeRotationY = 180; + } else if (min == hWest) { + slopeRotationY = 270; + } + + double newRotation = config.getRotation().getYAxis().getMin() + slopeRotationY; + if (newRotation == 0) { + config.getRotation().setYAxis(new IrisAxisRotationClamp(false, false, 0, 0, 90)); + config.getRotation().setEnabled(config.getRotation().canRotateX() || config.getRotation().canRotateZ()); + } else { + config.getRotation().setYAxis(new IrisAxisRotationClamp(true, false, newRotation, newRotation, 90)); + config.getRotation().setEnabled(true); + } + } + } + + if (config.isSmartBore()) { + ensureSmartBored(placer.isDebugSmartBore()); + } + + boolean warped = !config.getWarp().isFlat(); + boolean stilting = (config.getMode().equals(ObjectPlaceMode.STILT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT) || + config.getMode() == ObjectPlaceMode.MIN_STILT || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT || + config.getMode() == ObjectPlaceMode.CENTER_STILT); + KMap heightmap = config.getSnow() > 0 ? new KMap<>() : null; + int spinx = rng.imax() / 1000; + int spiny = rng.imax() / 1000; + int spinz = rng.imax() / 1000; + int rty = config.getRotation().rotate(new BlockVector(0, getCenter().getBlockY(), 0), spinx, spiny, spinz).getBlockY(); + int ty = config.getTranslate().translate(new BlockVector(0, getCenter().getBlockY(), 0), config.getRotation(), spinx, spiny, spinz).getBlockY(); + int y = -1; + int xx, zz; + int yrand = config.getTranslate().getYRandom(); + yrand = yrand > 0 ? rng.i(0, yrand) : yrand < 0 ? rng.i(yrand, 0) : yrand; + boolean bail = false; + + 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; + } + } 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()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + 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 (h > y) + y = h; + } + } + } else if (config.getMode().equals(ObjectPlaceMode.FAST_MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT)) { + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + + int xRadius = (rotatedDimensions.getBlockX() / 2); + int xLength = xRadius + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zRadius = (rotatedDimensions.getBlockZ() / 2); + int zLength = zRadius + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + + 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 (h > y) + y = h; + } + } + } else if (config.getMode().equals(ObjectPlaceMode.MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.MIN_STILT) { + y = rdata.getEngine().getHeight() + 1; + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + + int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + 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 (h < y) { + y = h; + } + } + } + } else if (config.getMode().equals(ObjectPlaceMode.FAST_MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT) { + y = rdata.getEngine().getHeight() + 1; + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + + int xRadius = (rotatedDimensions.getBlockX() / 2); + int xLength = xRadius + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zRadius = (rotatedDimensions.getBlockZ() / 2); + int zLength = zRadius + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + + 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 (h < y) { + y = h; + } + } + } + } 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; + } + } + } 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 (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 (bail) { + return -1; + } + + if (yv < 0) { + if (!config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) { + return -1; + } + } + + if (c != null && Math.max(0, h + yrand + ty) + 1 >= c.getHeight()) { + return -1; + } + + if (config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) { + return -1; + } + + if (!config.getClamp().canPlace(y + rty + ty, y - rty + ty)) { + return -1; + } + + if (!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++) { + for (int j = y - Math.floorDiv(h, 2) + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { + for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { + PlacedObject p = engine.getObjectPlacement(i, j, k); + if (p == null) continue; + IrisObject o = p.getObject(); + if (o == null) continue; + String key = o.getLoadKey(); + if (key != null) { + if (config.getForbiddenCollisions().contains(key) && !config.getAllowedCollisions().contains(key)) { + // Iris.debug("%s collides with %s (%s / %s / %s)", getLoadKey(), key, i, j, k); + return -1; + } + } + } + } + } + } + + if (config.isBore()) { + 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++) { + for (int j = y - Math.floorDiv(h, 2) - config.getBoreExtendMinY() + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) + config.getBoreExtendMaxY() - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { + for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { + placer.set(i, j, k, AIR); + } + } + } + } + + int lowest = Integer.MAX_VALUE; + y += yrand; + readLock.lock(); + + KMap markers = null; + + try { + if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) { + markers = new KMap<>(); + for (IrisObjectMarker j : config.getMarkers()) { + IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker()); + + if (marker == null) { + continue; + } + + int max = j.getMaximumMarkers(); + + for (BlockVector i : getBlocks().k().shuffle()) { + if (max <= 0) { + break; + } + + BlockData data = getBlocks().get(i); + + for (BlockData k : j.getMark(rdata)) { + if (max <= 0) { + break; + } + + if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { + boolean a = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 1, 0)))); + boolean fff = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 2, 0)))); + + if (!marker.isEmptyAbove() || (a && fff)) { + markers.put(i, j.getMarker()); + max--; + } + } + } + } + } + } + + for (BlockVector g : getBlocks().keySet()) { + BlockData d; + TileData tile = null; + + try { + d = getBlocks().get(g); + tile = getStates().get(g); + } catch (Throwable e) { + Iris.reportError(e); + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)"); + d = AIR; + } + + if (d == null) { + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (null)"); + d = AIR; + } + + BlockVector i = g.clone(); + BlockData data = d.clone(); + i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); + i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); + + if (stilting && i.getBlockY() < lowest && !B.isAir(data)) { + lowest = i.getBlockY(); + } + + if (placer.isPreventingDecay() && (data) instanceof Leaves && !((Leaves) (data)).isPersistent()) { + ((Leaves) data).setPersistent(true); + } + + for (IrisObjectReplace j : config.getEdit()) { + if (rng.chance(j.getChance())) { + for (BlockData k : j.getFind(rdata)) { + if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { + BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); + + if (newData.getMaterial() == data.getMaterial() && !(newData instanceof IrisBlockData || data instanceof IrisBlockData)) + data = data.merge(newData); + else + data = newData; + + if (newData.getMaterial() == Material.SPAWNER) { + Optional> t = j.getReplace().getTile(rng, x, y, z, rdata); + if (t.isPresent()) { + tile = t.get(); + } + } + } + } + } + } + + data = config.getRotation().rotate(data, spinx, spiny, spinz); + xx = x + (int) Math.round(i.getX()); + + int yy = y + (int) Math.round(i.getY()); + zz = z + (int) Math.round(i.getZ()); + + if (warped) { + xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); + zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); + } + + if (yv < 0 && (config.getMode().equals(ObjectPlaceMode.PAINT)) && !B.isVineBlock(data)) { + yy = (int) Math.round(i.getY()) + Math.floorDiv(h, 2) + placer.getHighest(xx, zz, getLoader(), config.isUnderwater()); + } + + if (heightmap != null) { + Position2 pos = new Position2(xx, zz); + + if (!heightmap.containsKey(pos)) { + heightmap.put(pos, yy); + } + + if (heightmap.get(pos) < yy) { + heightmap.put(pos, yy); + } + } + + if (config.isMeld() && !placer.isSolid(xx, yy, zz)) { + continue; + } + + if ((config.isWaterloggable() || config.isUnderwater()) && yy <= placer.getFluidHeight() && data instanceof Waterlogged) { + // TODO Here + ((Waterlogged) data).setWaterlogged(true); + } + + if (B.isVineBlock(data)) { + MultipleFacing f = (MultipleFacing) data; + for (BlockFace face : f.getAllowedFaces()) { + BlockData facingBlock = placer.get(xx + face.getModX(), yy + face.getModY(), zz + face.getModZ()); + if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { + f.setFace(face, true); + } + } + } + + if (listener != null) { + listener.accept(new BlockPosition(xx, yy, zz), data); + } + + if (markers != null && markers.containsKey(g)) { + placer.getEngine().getMantle().getMantle().set(xx, yy, zz, new MatterMarker(markers.get(g))); + } + + boolean wouldReplace = B.isSolid(placer.get(xx, yy, zz)) && B.isVineBlock(data); + + if (!data.getMaterial().equals(Material.AIR) && !data.getMaterial().equals(Material.CAVE_AIR) && !wouldReplace) { + placer.set(xx, yy, zz, data); + if (tile != null) { + placer.setTile(xx, yy, zz, tile); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + Iris.reportError(e); + } + readLock.unlock(); + + if (stilting) { + readLock.lock(); + IrisStiltSettings settings = config.getStiltSettings(); + for (BlockVector g : getBlocks().keySet()) { + BlockData d; + + if (settings == null || settings.getPalette() == null) { + try { + d = getBlocks().get(g); + } catch (Throwable e) { + Iris.reportError(e); + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)"); + d = AIR; + } + + if (d == null) { + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt null)"); + d = AIR; + } + } else + d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata); + + + BlockVector i = g.clone(); + i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); + i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); + d = config.getRotation().rotate(d, spinx, spiny, spinz); + + if (i.getBlockY() != lowest) + continue; + + for (IrisObjectReplace j : config.getEdit()) { + if (rng.chance(j.getChance())) { + for (BlockData k : j.getFind(rdata)) { + if (j.isExact() ? k.matches(d) : k.getMaterial().equals(d.getMaterial())) { + BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); + + if (newData.getMaterial() == d.getMaterial()) { + d = d.merge(newData); + } else { + d = newData; + } + } + } + } + } + + if (d == null || B.isAir(d)) + continue; + + xx = x + (int) Math.round(i.getX()); + zz = z + (int) Math.round(i.getZ()); + + if (warped) { + xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); + zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); + } + + int highest = placer.getHighest(xx, zz, getLoader(), true); + + if ((config.isWaterloggable() || config.isUnderwater()) && highest <= placer.getFluidHeight() && d instanceof Waterlogged) + ((Waterlogged) d).setWaterlogged(true); + + if (yv >= 0 && config.isBottom()) + y += Math.floorDiv(h, 2); + + int lowerBound = highest - 1; + if (settings != null) { + lowerBound -= config.getStiltSettings().getOverStilt() - rng.i(0, config.getStiltSettings().getYRand()); + if (settings.getYMax() != 0) + lowerBound -= Math.min(config.getStiltSettings().getYMax() - (lowest + y - highest), 0); + } + for (int j = lowest + y; j > lowerBound; j--) { + if (B.isVineBlock(d)) { + MultipleFacing f = (MultipleFacing) d; + for (BlockFace face : f.getAllowedFaces()) { + BlockData facingBlock = placer.get(xx + face.getModX(), j + face.getModY(), zz + face.getModZ()); + if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { + f.setFace(face, true); + } + } + } + placer.set(xx, j, zz, d); + } + + } + + readLock.unlock(); + } + + if (heightmap != null) { + RNG rngx = rng.nextParallelRNG(3468854); + + for (Position2 i : heightmap.k()) { + int vx = i.getX(); + int vy = heightmap.get(i); + int vz = i.getZ(); + + if (config.getSnow() > 0) { + int height = rngx.i(0, (int) (config.getSnow() * 7)); + placer.set(vx, vy + 1, vz, SNOW_LAYERS[Math.max(Math.min(height, 7), 0)]); + } + } + } + + return y; + } + + public IrisObject rotateCopy(IrisObjectRotation rt) { + IrisObject copy = copy(); + copy.rotate(rt, 0, 0, 0); + return copy; + } + + public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) { + KMap d = new KMap<>(); + + for (BlockVector i : getBlocks().keySet()) { + d.put(r.rotate(i.clone(), spinx, spiny, spinz), r.rotate(getBlocks().get(i).clone(), + spinx, spiny, spinz)); + } + + KMap> dx = new KMap<>(); + + for (BlockVector i : getStates().keySet()) { + dx.put(r.rotate(i.clone(), spinx, spiny, spinz), getStates().get(i)); + } + + blocks = d; + states = dx; + } + + public void place(Location at) { + for (BlockVector i : getBlocks().keySet()) { + Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock(); + b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); + + if (getStates().containsKey(i)) { + Iris.info(Objects.requireNonNull(states.get(i)).toString()); + BlockState st = b.getState(); + Objects.requireNonNull(getStates().get(i)).toBukkitTry(st); + st.update(); + } + } + } + + public void placeCenterY(Location at) { + for (BlockVector i : getBlocks().keySet()) { + Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock(); + b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); + + if (getStates().containsKey(i)) { + Objects.requireNonNull(getStates().get(i)).toBukkitTry(b.getState()); + } + } + } + + public synchronized KMap getBlocks() { + return blocks; + } + + public synchronized KMap> getStates() { + return states; + } + + public void unplaceCenterY(Location at) { + for (BlockVector i : getBlocks().keySet()) { + at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false); + } + } + + public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) { + Vector sm1 = new Vector(scale - 1, scale - 1, scale - 1); + scale = Math.max(0.001, Math.min(50, scale)); + if (scale < 1) { + scale = scale - 0.0001; + } + + IrisPosition l1 = getAABB().max(); + IrisPosition l2 = getAABB().min(); + @SuppressWarnings({"unchecked", "rawtypes"}) HashMap placeBlock = new HashMap(); + + Vector center = getCenter(); + if (getH() == 2) { + center = center.setY(center.getBlockY() + 0.5); + } + if (getW() == 2) { + center = center.setX(center.getBlockX() + 0.5); + } + if (getD() == 2) { + center = center.setZ(center.getBlockZ() + 0.5); + } + + IrisObject oo = new IrisObject((int) Math.ceil((w * scale) + (scale * 2)), (int) Math.ceil((h * scale) + (scale * 2)), (int) Math.ceil((d * scale) + (scale * 2))); + + for (Map.Entry entry : blocks.entrySet()) { + BlockData bd = entry.getValue(); + placeBlock.put(entry.getKey().clone().add(HALF).subtract(center) + .multiply(scale).add(sm1).toBlockVector(), bd); + } + + for (Map.Entry entry : placeBlock.entrySet()) { + BlockVector v = entry.getKey(); + if (scale > 1) { + for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) { + oo.getBlocks().put(vec, entry.getValue()); + } + } else { + oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue()); + } + } + + if (scale > 1) { + switch (interpolation) { + case TRILINEAR -> oo.trilinear((int) Math.round(scale)); + case TRICUBIC -> oo.tricubic((int) Math.round(scale)); + case TRIHERMITE -> oo.trihermite((int) Math.round(scale)); + } + } + + return oo; + } + + public void trilinear(int rad) { + KMap v = getBlocks().copy(); + KMap b = new KMap<>(); + BlockVector min = getAABB().minbv(); + BlockVector max = getAABB().maxbv(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (IrisInterpolation.getTrilinear(x, y, z, rad, (xx, yy, zz) -> { + BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); + + if (data == null || data.getMaterial().isAir()) { + return 0; + } + + return 1; + }) >= 0.5) { + b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); + } else { + b.put(new BlockVector(x, y, z), AIR); + } + } + } + } + + blocks = b; + } + + public void tricubic(int rad) { + KMap v = getBlocks().copy(); + KMap b = new KMap<>(); + BlockVector min = getAABB().minbv(); + BlockVector max = getAABB().maxbv(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (IrisInterpolation.getTricubic(x, y, z, rad, (xx, yy, zz) -> { + BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); + + if (data == null || data.getMaterial().isAir()) { + return 0; + } + + return 1; + }) >= 0.5) { + b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); + } else { + b.put(new BlockVector(x, y, z), AIR); + } + } + } + } + + blocks = b; + } + + public void trihermite(int rad) { + trihermite(rad, 0D, 0D); + } + + public void trihermite(int rad, double tension, double bias) { + KMap v = getBlocks().copy(); + KMap b = new KMap<>(); + BlockVector min = getAABB().minbv(); + BlockVector max = getAABB().maxbv(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (IrisInterpolation.getTrihermite(x, y, z, rad, (xx, yy, zz) -> { + BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); + + if (data == null || data.getMaterial().isAir()) { + return 0; + } + + return 1; + }, tension, bias) >= 0.5) { + b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); + } else { + b.put(new BlockVector(x, y, z), AIR); + } + } + } + } + + blocks = b; + } + + private BlockData nearestBlockData(int x, int y, int z) { + BlockVector vv = new BlockVector(x, y, z); + BlockData r = getBlocks().get(vv); + + if (r != null && !r.getMaterial().isAir()) { + return r; + } + + double d = Double.MAX_VALUE; + + for (Map.Entry entry : blocks.entrySet()) { + BlockData dat = entry.getValue(); + + if (dat.getMaterial().isAir()) { + continue; + } + + double dx = entry.getKey().distanceSquared(vv); + + if (dx < d) { + d = dx; + r = dat; + } + } + + return r; + } + + public int volume() { + return blocks.size(); + } + + @Override + public String getFolderName() { + return "objects"; + } + + @Override + public String getTypeName() { + return "Object"; + } + + @Override + public void scanForErrors(JSONObject p, VolmitSender sender) { + } +} From 5b4265783ea9b9b9947d2bca1f47202863611b4c Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 1 May 2024 18:42:23 +0200 Subject: [PATCH 13/26] fix object w/h/d not changing with rotation --- core/src/main/java/com/volmit/iris/engine/object/IrisObject.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index ea3d115ea..0e8b3b907 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -1036,6 +1036,7 @@ public class IrisObject extends IrisRegistrant { blocks = d; states = dx; + shrinkwrap(); } public void place(Location at) { From 40163d25b800496fb1ce860c9efd70c12f57c726 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 1 May 2024 18:44:45 +0200 Subject: [PATCH 14/26] add object shink command for fixing too large/small bounding boxes for objects --- .../volmit/iris/core/commands/CommandObject.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java b/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java index ea7f07f7a..15bf87b76 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java @@ -197,6 +197,20 @@ public class CommandObject implements DecreeExecutor { } } + @Decree(description = "Shrink an object to its minimum size") + public void shrink(@Param(description = "The object to shrink", customHandler = ObjectHandler.class) String object) { + IrisObject o = IrisData.loadAnyObject(object); + sender().sendMessage("Current Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD()); + o.shrinkwrap(); + sender().sendMessage("New Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD()); + try { + o.write(o.getLoadFile()); + } catch (IOException e) { + sender().sendMessage("Failed to save object " + o.getLoadFile() + ": " + e.getMessage()); + e.printStackTrace(); + } + } + @Decree(description = "Get a powder that reveals objects", studio = true, aliases = "d") public void dust() { player().getInventory().addItem(WandSVC.createDust()); From c653d852e43368eb3a8dcb970eef01031acabcac Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 1 May 2024 18:46:22 +0200 Subject: [PATCH 15/26] make jigsaw place command use the same method as normal gen --- .../iris/core/commands/CommandJigsaw.java | 2 +- .../iris/engine/jigsaw/PlannedStructure.java | 99 ++++++++++++++++--- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java index edab02cdb..70e8e2ae3 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java @@ -59,7 +59,7 @@ public class CommandJigsaw implements DecreeExecutor { 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 == 0 ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place " + failed + " pieces!")); + ps.place(world(), failed -> sender.sendMessage(failed ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place the structure!")); } @Decree(description = "Create a jigsaw piece") diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index dc2bf6b68..18c03bd9b 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -21,6 +21,7 @@ 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.object.*; @@ -30,13 +31,17 @@ import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; +import com.volmit.iris.util.scheduling.J; import lombok.Data; -import lombok.EqualsAndHashCode; 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.concurrent.atomic.AtomicInteger; -import java.util.function.IntConsumer; +import java.util.function.Consumer; @Data public class PlannedStructure { @@ -139,20 +144,86 @@ public class PlannedStructure { 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()) != -1; + }, null, getData().getEngine() != null ? getData() : eng.getData()) != -1; } - public void place(World world, IntConsumer consumer) { - AtomicInteger processed = new AtomicInteger(); - AtomicInteger failures = new AtomicInteger(); - for (PlannedPiece i : pieces) { - Iris.sq(() -> { - if (!i.place(world)) failures.incrementAndGet(); - if (processed.incrementAndGet() == pieces.size()) { - consumer.accept(failures.get()); - } - }); + public void place(World world, Consumer 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 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))); } private void generateOutwards() { From 93469fb3b45228417bddfe1212a999b1d0d79c92 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Wed, 1 May 2024 20:01:10 +0200 Subject: [PATCH 16/26] include translate option into the bounding box of jigsaw pieces --- .../com/volmit/iris/engine/jigsaw/PlannedPiece.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index 08088ac38..cf0ef0dc0 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -97,7 +97,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; } From 49a65521682ae1831c7783b972eb7f8d11028e35 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 10 May 2024 15:41:42 +0200 Subject: [PATCH 17/26] add forcePlace option to suppress placement checks --- .../volmit/iris/engine/object/IrisObject.java | 68 ++++++++++++------- .../engine/object/IrisObjectPlacement.java | 2 + 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index 0e8b3b907..cb85b0d66 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -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++) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java index 813ef8c4a..d03248e87 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java @@ -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 forbiddenCollisions = new KList<>(); + @Desc("Ignore any placement restrictions for this object") + private boolean forcePlace = false; private transient AtomicCache cache = new AtomicCache<>(); public IrisObjectPlacement toPlacement(String... place) { From b70e94dc65648c56a987b89558263194ceabea40 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 10 May 2024 15:43:01 +0200 Subject: [PATCH 18/26] make manual jigsaw place use the same calculations as normal --- .../iris/core/commands/CommandJigsaw.java | 15 ++- .../framework/placer/WorldObjectPlacer.java | 127 ++++++++++++++++++ .../iris/engine/jigsaw/PlannedPiece.java | 112 --------------- .../iris/engine/jigsaw/PlannedStructure.java | 89 +----------- 4 files changed, 141 insertions(+), 202 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java index 70e8e2ae3..710184027 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java @@ -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") diff --git a/core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java b/core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java new file mode 100644 index 000000000..16968eee5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java @@ -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 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 tile) { + BlockState state = world.getBlockAt(xx, yy + world.getMinHeight(), zz).getState(); + tile.toBukkitTry(state); + state.update(); + } +} diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index cf0ef0dc0..862674f8b 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -20,24 +20,11 @@ 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.math.AxisAlignedBB; -import com.volmit.iris.util.math.BlockPosition; -import com.volmit.iris.util.math.RNG; 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 org.bukkit.util.BlockVector; import java.util.ArrayList; @@ -175,103 +162,4 @@ public class PlannedPiece { public boolean isFull() { return connected.size() >= piece.getConnectors().size() || isDead(); } - - 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 - } - 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 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; - } } diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index 18c03bd9b..846ba406a 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -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; @@ -119,7 +112,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; @@ -147,83 +139,8 @@ public class PlannedStructure { }, null, getData().getEngine() != null ? getData() : eng.getData()) != -1; } - public void place(World world, Consumer 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 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 consumer) { + J.s(() -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine()))); } private void generateOutwards() { From 8b5d1d0298902469ae32d56678e40623f8854c9e Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 10 May 2024 15:58:26 +0200 Subject: [PATCH 19/26] rewrite jigsaw placement logic to prevent placements from being too close --- .../components/MantleJigsawComponent.java | 81 ++++++++----------- .../object/IrisJigsawStructurePlacement.java | 61 +++++++++++++- 2 files changed, 93 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index f10ff67c7..b7d8223b7 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -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 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 cachedRegions = new KSet<>(); KMap> cache = new KMap<>(); KMap 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 structures, + private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList structures, KSet cachedRegions, KMap> cache, KMap 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 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(); + } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java index 16eea76e8..f5a10361a 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java @@ -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 minDistances = new KList<>(); @@ -64,4 +84,39 @@ 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; + } + + public enum SpreadType { + LINEAR(RNG::i), + 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); + } } From 55c0fa5f320ed5058da76dffed4f9feaf7123570 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 10 May 2024 15:58:57 +0200 Subject: [PATCH 20/26] add command to sample the distances between structures --- .../iris/core/commands/CommandStudio.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index bea1dcd2b..774e760b8 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -74,6 +74,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; @@ -332,6 +333,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> 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 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( @Param(name = "world", description = "The world to open the generator for", contextual = true) From f68d7420e3a95139461bc3236ee6dfbe95a4e14a Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Sat, 11 May 2024 12:50:40 +0200 Subject: [PATCH 21/26] woops forgot description for SpreadType --- .../iris/engine/object/IrisJigsawStructurePlacement.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java index f5a10361a..ed62db9c0 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java @@ -102,8 +102,11 @@ public class IrisJigsawStructurePlacement implements IRare { 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; From 09a0f830137bf67628c8b0264c1ef6653ae02729 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Sat, 11 May 2024 12:51:29 +0200 Subject: [PATCH 22/26] add terminating pool overwrite --- .../java/com/volmit/iris/engine/jigsaw/PlannedPiece.java | 2 +- .../com/volmit/iris/engine/jigsaw/PlannedStructure.java | 7 +++++-- .../com/volmit/iris/engine/object/IrisJigsawStructure.java | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index 862674f8b..882f29f02 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -160,6 +160,6 @@ public class PlannedPiece { } public boolean isFull() { - return connected.size() >= piece.getConnectors().size() || isDead(); + return connected.size() >= piece.getConnectors().size(); } } diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index 846ba406a..594f14c91 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -257,7 +257,8 @@ public class PlannedStructure { private KList getShuffledPiecesFor(IrisJigsawPieceConnector c) { KList p = new KList<>(); - for (String i : c.getPools().shuffleCopy(rng)) { + KList 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); @@ -283,7 +284,9 @@ public class PlannedStructure { } public KList getPiecesWithAvailableConnectors() { - return pieces.copy().removeWhere(PlannedPiece::isFull); + KList available = pieces.copy().removeWhere(PlannedPiece::isFull); + if (!terminating) available.removeIf(PlannedPiece::isDead); + return available; } public int getVolume() { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java index 8a009364f..9d6d28506 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java @@ -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; From 6ddb0b5304826e51fa3ca59f97da2ae695c458db Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Thu, 16 May 2024 13:12:11 +0200 Subject: [PATCH 23/26] update gson library --- build.gradle | 2 +- core/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 2484924d8..a012fdd3a 100644 --- a/build.gradle +++ b/build.gradle @@ -244,7 +244,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' diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 83ad85017..a54d30829 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -12,7 +12,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 From 44500d6af910fb28f73b96721c690b237e9a46b7 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Thu, 16 May 2024 13:12:49 +0200 Subject: [PATCH 24/26] fix translation not rotating --- .../com/volmit/iris/engine/object/IrisObjectRotation.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java index 5b75ef725..6e8eb2a11 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java @@ -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; } From fd10c005b0cdadc8b377b09e25a0434ab320aef4 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Thu, 16 May 2024 13:15:34 +0200 Subject: [PATCH 25/26] disable warp to prevent x/z offsets --- .../java/com/volmit/iris/engine/jigsaw/PlannedStructure.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index 594f14c91..05b199a3a 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -103,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()); } From 6348442962af4cd8ab9b991df8b6271aa9540982 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Thu, 16 May 2024 13:17:17 +0200 Subject: [PATCH 26/26] add y lock to fix offset when placing objects --- .../iris/engine/jigsaw/PlannedPiece.java | 52 +++++++++++++++++-- .../iris/engine/jigsaw/PlannedStructure.java | 15 +++++- .../object/IrisJigsawPieceConnector.java | 3 ++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index 882f29f02..5a966e752 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -22,15 +22,17 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.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; -@SuppressWarnings("ALL") @Data public class PlannedPiece { private IrisPosition position; @@ -45,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 realPositions; public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece) { this(structure, position, piece, 0, 0, 0); @@ -66,6 +74,7 @@ public class PlannedPiece { this.object.setLoadKey(piece.getObject()); this.ogObject.setLoadKey(piece.getObject()); this.connected = new KList<>(); + this.realPositions = new KMap<>(); } @@ -124,11 +133,21 @@ public class PlannedPiece { return c; } - public boolean connect(IrisJigsawPieceConnector c) { - if (piece.getConnectors().contains(c)) { - return connected.addIfMissing(c); - } + public KList getChildConnectors() { + ParentConnection pc = getParent(); + KList 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; } @@ -162,4 +181,27 @@ public class PlannedPiece { 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())); + } + } } diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index 05b199a3a..77d4157f7 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -126,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)) { @@ -134,6 +145,7 @@ 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); @@ -248,8 +260,7 @@ public class PlannedStructure { return false; } - piece.connect(pieceConnector); - test.connect(testConnector); + piece.connect(pieceConnector, test, testConnector); pieces.add(test); return true; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java index bc5fce61b..f3afe074a 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java @@ -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() + ")"; }