From b1d2ac7822daace8737c680b8c62d6e0c75c1426 Mon Sep 17 00:00:00 2001 From: cyberpwn Date: Mon, 13 Sep 2021 20:36:50 -0400 Subject: [PATCH] Hella nice goto --- .../iris/core/commands/CommandFind.java | 104 ++++++++++++++ .../iris/core/commands/CommandIris.java | 1 + .../iris/core/commands/CommandStudio.java | 55 -------- .../com/volmit/iris/engine/IrisEngine.java | 1 - .../volmit/iris/engine/framework/Engine.java | 130 +++++++++++++++++- .../volmit/iris/engine/framework/Locator.java | 95 +++++++++++-- .../com/volmit/iris/util/math/Spiral.java | 11 +- 7 files changed, 326 insertions(+), 71 deletions(-) create mode 100644 src/main/java/com/volmit/iris/core/commands/CommandFind.java 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