diff --git a/src/main/java/com/volmit/iris/core/commands/CommandFind.java b/src/main/java/com/volmit/iris/core/commands/CommandFind.java
new file mode 100644
index 000000000..8a2760b3b
--- /dev/null
+++ b/src/main/java/com/volmit/iris/core/commands/CommandFind.java
@@ -0,0 +1,104 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2021 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.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.Engine;
+import com.volmit.iris.engine.jigsaw.PlannedStructure;
+import com.volmit.iris.engine.object.IrisBiome;
+import com.volmit.iris.engine.object.IrisJigsawPiece;
+import com.volmit.iris.engine.object.IrisJigsawStructure;
+import com.volmit.iris.engine.object.IrisObject;
+import com.volmit.iris.engine.object.IrisPosition;
+import com.volmit.iris.engine.object.IrisRegion;
+import com.volmit.iris.util.decree.DecreeExecutor;
+import com.volmit.iris.util.decree.DecreeOrigin;
+import com.volmit.iris.util.decree.annotations.Decree;
+import com.volmit.iris.util.decree.annotations.Param;
+import com.volmit.iris.util.decree.specialhandlers.ObjectHandler;
+import com.volmit.iris.util.format.C;
+
+@Decree(name = "find", origin = DecreeOrigin.PLAYER, description = "Iris Find commands")
+public class CommandFind implements DecreeExecutor {
+ @Decree(description = "Find a biome")
+ public void biome(
+ @Param(description = "The biome to look for")
+ IrisBiome biome
+ ) {
+ Engine e = engine();
+
+ if(e == null)
+ {
+ sender().sendMessage(C.GOLD + "Not in an Iris World!");
+ return;
+ }
+
+ e.gotoBiome(biome, player());
+ }
+
+ @Decree(description = "Find a region")
+ public void region(
+ @Param(description = "The region to look for")
+ IrisRegion region
+ ) {
+ Engine e = engine();
+
+ if(e == null)
+ {
+ sender().sendMessage(C.GOLD + "Not in an Iris World!");
+ return;
+ }
+
+ e.gotoRegion(region, player());
+ }
+
+ @Decree(description = "Find a structure")
+ public void structure(
+ @Param(description = "The structure to look for")
+ IrisJigsawStructure structure
+ ) {
+ Engine e = engine();
+
+ if(e == null)
+ {
+ sender().sendMessage(C.GOLD + "Not in an Iris World!");
+ return;
+ }
+
+ e.gotoJigsaw(structure, player());
+ }
+
+ @Decree(description = "Find an object")
+ public void object(
+ @Param(description = "The object to look for", customHandler = ObjectHandler.class)
+ String object
+ ) {
+ Engine e = engine();
+
+ if(e == null)
+ {
+ sender().sendMessage(C.GOLD + "Not in an Iris World!");
+ return;
+ }
+
+ e.gotoObject(object, player());
+ }
+}
diff --git a/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/src/main/java/com/volmit/iris/core/commands/CommandIris.java
index a51b68bd5..45a360f8f 100644
--- a/src/main/java/com/volmit/iris/core/commands/CommandIris.java
+++ b/src/main/java/com/volmit/iris/core/commands/CommandIris.java
@@ -52,6 +52,7 @@ public class CommandIris implements DecreeExecutor {
private CommandObject object;
private CommandJigsaw jigsaw;
private CommandWhat what;
+ private CommandFind find;
@Decree(description = "Create a new world", aliases = {"+", "c"})
public void create(
diff --git a/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/src/main/java/com/volmit/iris/core/commands/CommandStudio.java
index 8a1199ca4..48633a3cb 100644
--- a/src/main/java/com/volmit/iris/core/commands/CommandStudio.java
+++ b/src/main/java/com/volmit/iris/core/commands/CommandStudio.java
@@ -365,61 +365,6 @@ public class CommandStudio implements DecreeExecutor {
NoiseExplorerGUI.launch(l, "Custom Generator");
}
- @Decree(description = "Find any biome or region", aliases = {"goto", "g"}, origin = DecreeOrigin.PLAYER)
- public void find(
- @Param(description = "The biome or region to find", defaultValue = "null")
- IrisBiome biome,
- @Param(description = "The region to find", defaultValue = "null")
- IrisRegion region
- ) {
- if (!IrisToolbelt.isIrisWorld(world())) {
- sender().sendMessage(C.RED + "You must be in an Iris world to use this command!");
- return;
- }
-
- if (biome == null && region == null) {
- sender().sendMessage(C.RED + "You must specify a biome= or region=!");
- return;
- }
-
- IrisPosition regionPosition = null;
- if (region != null) {
- regionPosition = engine().lookForRegion(region, 10000, (v) -> sender().sendMessage("Looking for the " + C.BOLD + C.WHITE + region.getName() + C.RESET + C.GRAY + " region: Checked " + Form.f(v) + " Places"));
- if (regionPosition == null) {
- sender().sendMessage(C.YELLOW + "Couldn't find the " + region.getName() + " region.");
- } else {
- sender().sendMessage(C.GREEN + "Found the " + region.getName() + " region!.");
- }
- }
-
- IrisPosition biomePosition = null;
- if (biome != null) {
- biomePosition = engine().lookForBiome(biome, 10000, (v) -> sender().sendMessage("Looking for the " + C.BOLD + C.WHITE + biome.getName() + C.RESET + C.GRAY + " biome: Checked " + Form.f(v) + " Places"));
- if (biomePosition == null) {
- sender().sendMessage(C.YELLOW + "Couldn't find the " + biome.getName() + " biome.");
- } else {
- sender().sendMessage(C.GREEN + "Found the " + biome.getName() + " biome!.");
- }
- }
-
- if (regionPosition == null && region != null) {
- sender().sendMessage(C.RED + "Could not find the region you specified.");
- } else if (regionPosition != null) {
- sender().sendMessage(C.GREEN + "Found the region at: " + regionPosition);
- }
- if (biomePosition == null && biome != null) {
- sender().sendMessage(C.RED + "Could not find the biome you specified.");
- } else if (biomePosition != null) {
- sender().sendMessage(C.GREEN + "Found the biome at: " + biomePosition);
- }
-
- final IrisPosition finalL = regionPosition == null ? biomePosition : regionPosition;
- if (finalL == null) {
- return;
- }
- J.s(() -> player().teleport(finalL.toLocation(world())));
- }
-
@Decree(description = "Hotload a studio", aliases = "reload", origin = DecreeOrigin.PLAYER)
public void hotload() {
if (noStudio()) return;
diff --git a/src/main/java/com/volmit/iris/engine/IrisEngine.java b/src/main/java/com/volmit/iris/engine/IrisEngine.java
index d3c6ec44d..4cd10363a 100644
--- a/src/main/java/com/volmit/iris/engine/IrisEngine.java
+++ b/src/main/java/com/volmit/iris/engine/IrisEngine.java
@@ -47,7 +47,6 @@ import com.volmit.iris.engine.object.IrisBiomePaletteLayer;
import com.volmit.iris.engine.object.IrisDecorator;
import com.volmit.iris.engine.object.IrisEngineData;
import com.volmit.iris.engine.object.IrisJigsawStructure;
-import com.volmit.iris.engine.object.IrisObject;
import com.volmit.iris.engine.object.IrisObjectPlacement;
import com.volmit.iris.engine.scripting.EngineExecutionEnvironment;
import com.volmit.iris.util.atomics.AtomicRollingSequence;
diff --git a/src/main/java/com/volmit/iris/engine/framework/Engine.java b/src/main/java/com/volmit/iris/engine/framework/Engine.java
index a631c2ced..66910955f 100644
--- a/src/main/java/com/volmit/iris/engine/framework/Engine.java
+++ b/src/main/java/com/volmit/iris/engine/framework/Engine.java
@@ -22,6 +22,7 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.gui.components.RenderType;
import com.volmit.iris.core.gui.components.Renderer;
import com.volmit.iris.core.loader.IrisData;
+import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.data.chunk.TerrainChunk;
@@ -32,6 +33,7 @@ import com.volmit.iris.engine.object.IrisColor;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisEngineData;
import com.volmit.iris.engine.object.IrisJigsawStructure;
+import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
import com.volmit.iris.engine.object.IrisLootMode;
import com.volmit.iris.engine.object.IrisLootReference;
import com.volmit.iris.engine.object.IrisLootTable;
@@ -46,8 +48,10 @@ 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.DataProvider;
+import com.volmit.iris.util.decree.handlers.JigsawStructureHandler;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates;
+import com.volmit.iris.util.format.C;
import com.volmit.iris.util.function.Function2;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.MantleFlag;
@@ -72,6 +76,7 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
@@ -79,7 +84,6 @@ import org.bukkit.inventory.ItemStack;
import java.awt.Color;
import java.util.Arrays;
-import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -87,6 +91,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdater, Renderer, Hotloadable {
KList getStages();
@@ -780,4 +785,127 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
default IrisBiome getBiomeOrMantle(Location l) {
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
}
+
+ default void gotoBiome(IrisBiome biome, Player player)
+ {
+ Set regionKeys = getDimension()
+ .getAllRegions(this).stream()
+ .filter((i) -> i.getAllBiomes(this).contains(biome))
+ .map(IrisRegistrant::getLoadKey)
+ .collect(Collectors.toSet());
+ Locator lb = Locator.surfaceBiome(biome.getLoadKey());
+ Locator locator = (engine, chunk)
+ -> regionKeys.contains(getRegion((chunk.getX()<< 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey())
+ && lb.matches(engine, chunk);
+
+ if(!regionKeys.isEmpty())
+ {
+ locator.find(player);
+ }
+
+ else
+ {
+ player.sendMessage(C.RED + biome.getName() + " is not in any defined regions!");
+ }
+ }
+
+ default void gotoJigsaw(IrisJigsawStructure s, Player player)
+ {
+ if(getDimension().getJigsawStructures().stream()
+ .map(IrisJigsawStructurePlacement::getStructure)
+ .collect(Collectors.toSet()).contains(s.getLoadKey()))
+ {
+ Locator.jigsawStructure(s.getLoadKey()).find(player);
+ }
+
+ else
+ {
+ Set biomeKeys = getDimension().getAllBiomes(this).stream()
+ .filter((i) -> i.getJigsawStructures()
+ .stream()
+ .anyMatch((j) -> j.getStructure().equals(s.getLoadKey())))
+ .map(IrisRegistrant::getLoadKey)
+ .collect(Collectors.toSet());
+ Set regionKeys = getDimension().getAllRegions(this).stream()
+ .filter((i) -> i.getAllBiomeIds().stream().anyMatch(biomeKeys::contains)
+ || i.getJigsawStructures()
+ .stream()
+ .anyMatch((j) -> j.getStructure().equals(s.getLoadKey())))
+ .map(IrisRegistrant::getLoadKey)
+ .collect(Collectors.toSet());
+
+ Locator sl = Locator.jigsawStructure(s.getLoadKey());
+ Locator locator = (engine, chunk) -> {
+ if(biomeKeys.contains(getSurfaceBiome((chunk.getX()<< 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey()))
+ {
+ return sl.matches(engine, chunk);
+ }
+
+ else if(regionKeys.contains(getRegion((chunk.getX()<< 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey()))
+ {
+ return sl.matches(engine, chunk);
+ }
+ return false;
+ };
+
+ if(!regionKeys.isEmpty())
+ {
+ locator.find(player);
+ }
+
+ else
+ {
+ player.sendMessage(C.RED + s.getLoadKey() + " is not in any defined regions, biomes or dimensions!");
+ }
+ }
+
+ }
+
+ default void gotoObject(String s, Player player)
+ {
+ Set biomeKeys = getDimension().getAllBiomes(this).stream()
+ .filter((i) -> i.getObjects().stream().anyMatch((f) -> f.getPlace().contains(s)))
+ .map(IrisRegistrant::getLoadKey)
+ .collect(Collectors.toSet());
+ Set regionKeys = getDimension().getAllRegions(this).stream()
+ .filter((i) -> i.getAllBiomeIds().stream().anyMatch(biomeKeys::contains)
+ || i.getObjects().stream().anyMatch((f) -> f.getPlace().contains(s)))
+ .map(IrisRegistrant::getLoadKey)
+ .collect(Collectors.toSet());
+
+ Locator sl = Locator.object(s);
+ Locator locator = (engine, chunk) -> {
+ if(biomeKeys.contains(getSurfaceBiome((chunk.getX()<< 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey()))
+ {
+ return sl.matches(engine, chunk);
+ }
+
+ else if(regionKeys.contains(getRegion((chunk.getX()<< 4) + 8, (chunk.getZ() << 4) + 8).getLoadKey()))
+ {
+ return sl.matches(engine, chunk);
+ }
+ return false;
+ };
+
+ if(!regionKeys.isEmpty())
+ {
+ locator.find(player);
+ }
+
+ else
+ {
+ player.sendMessage(C.RED + s + " is not in any defined regions or biomes!");
+ }
+ }
+
+ default void gotoRegion(IrisRegion r, Player player)
+ {
+ if(!getDimension().getAllRegions(this).contains(r))
+ {
+ player.sendMessage(C.RED + r.getName() + " is not defined in the dimension!");
+ return;
+ }
+
+ Locator.region(r.getLoadKey()).find(player);
+ }
}
diff --git a/src/main/java/com/volmit/iris/engine/framework/Locator.java b/src/main/java/com/volmit/iris/engine/framework/Locator.java
index f285fccf6..2d955cbaf 100644
--- a/src/main/java/com/volmit/iris/engine/framework/Locator.java
+++ b/src/main/java/com/volmit/iris/engine/framework/Locator.java
@@ -18,52 +18,122 @@
package com.volmit.iris.engine.framework;
+import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
+import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisJigsawStructure;
+import com.volmit.iris.engine.object.IrisObject;
+import com.volmit.iris.engine.object.IrisRegion;
+import com.volmit.iris.util.format.Form;
+import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.Spiral;
+import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.matter.MatterCavern;
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.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
+import com.volmit.iris.util.scheduling.jobs.SingleJob;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
@FunctionalInterface
public interface Locator {
+ boolean matches(Engine engine, Position2 chunk);
+
static void cancelSearch()
{
if(LocatorCanceller.cancel != null)
{
LocatorCanceller.cancel.run();
+ LocatorCanceller.cancel = null;
}
}
- default Future find(Engine engine, Position2 pos, long timeout)
+ default void find(Player player)
{
+ find(player, 30_000);
+ }
+
+ default void find(Player player, long timeout)
+ {
+ AtomicLong checks = new AtomicLong();
+ long ms = M.ms();
+ new SingleJob("Searching", () -> {
+ try {
+ Position2 at = find(IrisToolbelt.access(player.getWorld()).getEngine(), new Position2(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4), timeout, checks::set).get();
+
+ if(at != null)
+ {
+ J.s(() -> player.teleport(new Location(player.getWorld(), (at.getX() << 4) + 8,
+ IrisToolbelt.access(player.getWorld()).getEngine().getHeight(
+ (at.getX() << 4) + 8,
+ (at.getZ() << 4) + 8, false),
+ (at.getZ() << 4) + 8)));
+ }
+ } catch (WrongEngineBroException | InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ }
+ }){
+ @Override
+ public String getName() {
+ return "Searched " + Form.f(checks.get()) + " Chunks";
+ }
+
+ @Override
+ public int getTotalWork() {
+ return (int) timeout;
+ }
+
+ @Override
+ public int getWorkCompleted() {
+ return (int) Math.min(M.ms() - ms, timeout-1);
+ }
+ }.execute(new VolmitSender(player));
+ }
+
+ default Future find(Engine engine, Position2 pos, long timeout, Consumer checks) throws WrongEngineBroException {
+ if(engine.isClosed())
+ {
+ throw new WrongEngineBroException();
+ }
+
+ cancelSearch();
+
return MultiBurst.burst.completeValue(() -> {
- int tc = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()) * 4;
+ int tc = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()) * 17;
MultiBurst burst = new MultiBurst("Iris Locator", Thread.MIN_PRIORITY);
AtomicBoolean found = new AtomicBoolean(false);
Position2 cursor = pos;
+ AtomicInteger searched = new AtomicInteger();
AtomicBoolean stop = new AtomicBoolean(false);
AtomicReference foundPos = new AtomicReference<>();
PrecisionStopwatch px = PrecisionStopwatch.start();
LocatorCanceller.cancel = () -> stop.set(true);
-
- while(!found.get() || stop.get() || px.getMilliseconds() > timeout)
+ AtomicReference next = new AtomicReference<>(cursor);
+ Spiraler s = new Spiraler(100000, 100000, (x, z) -> next.set(new Position2(x, z)));
+ s.setOffset(cursor.getX(), cursor.getZ());
+ s.next();
+ while(!found.get() && !stop.get() && px.getMilliseconds() < timeout)
{
BurstExecutor e = burst.burst(tc);
for(int i = 0; i < tc; i++)
{
- Position2 p = cursor;
- cursor = Spiral.next(cursor);
-
+ Position2 p = next.get();
+ s.next();
e.queue(() -> {
if(matches(engine, p))
{
@@ -74,12 +144,15 @@ public interface Locator {
found.set(true);
}
+ searched.incrementAndGet();
});
}
e.complete();
+ checks.accept(searched.get());
}
+ LocatorCanceller.cancel = null;
burst.close();
if(found.get() && foundPos.get() != null)
@@ -91,14 +164,12 @@ public interface Locator {
});
}
- boolean matches(Engine engine, Position2 chunk);
-
- static Locator region(String loadKey)
+ static Locator region(String loadKey)
{
return (e, c) -> e.getRegion((c.getX() << 4) + 8, (c.getZ() << 4) + 8).getLoadKey().equals(loadKey);
}
- static Locator jigsawStructure(String loadKey)
+ static Locator jigsawStructure(String loadKey)
{
return (e, c) -> {
IrisJigsawStructure s = e.getStructureAt(c.getX(), c.getZ());
@@ -106,7 +177,7 @@ public interface Locator {
};
}
- static Locator object(String loadKey)
+ static Locator object(String loadKey)
{
return (e, c) -> e.getObjectsAt(c.getX(), c.getZ()).contains(loadKey);
}
diff --git a/src/main/java/com/volmit/iris/util/math/Spiral.java b/src/main/java/com/volmit/iris/util/math/Spiral.java
index 6dd93e383..953ffa6ca 100644
--- a/src/main/java/com/volmit/iris/util/math/Spiral.java
+++ b/src/main/java/com/volmit/iris/util/math/Spiral.java
@@ -19,6 +19,8 @@
package com.volmit.iris.util.math;
import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
@@ -26,7 +28,12 @@ import java.util.Iterator;
/**
* Represents a spiraler which can start from any point within Long.MAX_VALUE by Long.MAX_VALUE and iterate anywhere.
*/
-public record Spiral(Position2 start, long max) implements Iterable {
+@Data
+@RequiredArgsConstructor
+public class Spiral implements Iterable {
+ private final Position2 start;
+ private final long max;
+
@SuppressWarnings("ConstantConditions")
public static Position2 next(Position2 p) {
int x = p.getX();
@@ -88,7 +95,7 @@ public record Spiral(Position2 start, long max) implements Iterable {
@Override
public boolean hasNext() {
- return itr < s.max();
+ return itr < s.getMax();
}
@Override