diff --git a/src/main/java/com/volmit/iris/Iris.java b/src/main/java/com/volmit/iris/Iris.java
index 3c158e99f..4dd11a222 100644
--- a/src/main/java/com/volmit/iris/Iris.java
+++ b/src/main/java/com/volmit/iris/Iris.java
@@ -26,7 +26,7 @@ import com.volmit.iris.core.link.BKLink;
import com.volmit.iris.core.link.MultiverseCoreLink;
import com.volmit.iris.core.link.MythicMobsLink;
import com.volmit.iris.core.nms.INMS;
-import com.volmit.iris.engine.IrisWorlds;
+import com.volmit.iris.core.tools.IrisWorlds;
import com.volmit.iris.engine.framework.EngineCompositeGenerator;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisBiomeCustom;
diff --git a/src/main/java/com/volmit/iris/core/tools/IrisCreator.java b/src/main/java/com/volmit/iris/core/tools/IrisCreator.java
new file mode 100644
index 000000000..1417e3fa7
--- /dev/null
+++ b/src/main/java/com/volmit/iris/core/tools/IrisCreator.java
@@ -0,0 +1,237 @@
+/*
+ * 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.tools;
+
+import com.volmit.iris.core.IrisSettings;
+import com.volmit.iris.core.pregenerator.PregenTask;
+import com.volmit.iris.engine.framework.IrisAccess;
+import com.volmit.iris.engine.headless.HeadlessWorld;
+import com.volmit.iris.engine.object.IrisDimension;
+import com.volmit.iris.util.exceptions.IrisException;
+import com.volmit.iris.util.exceptions.MissingDimensionException;
+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.J;
+import com.volmit.iris.util.scheduling.O;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import net.md_5.bungee.api.ChatMessageType;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.WorldCreator;
+
+import java.util.function.Consumer;
+
+/**
+ * Makes it a lot easier to setup an engine, world, studio or whatever
+ */
+@Data
+@Accessors(fluent = true, chain = true)
+public class IrisCreator {
+ /**
+ * Specify an area to pregenerate during creation
+ */
+ private PregenTask pregen;
+
+ /**
+ * Specify a sender to get updates & progress info + tp when world is created.
+ */
+ private VolmitSender sender;
+
+ /**
+ * The seed to use for this generator
+ */
+ private long seed = RNG.r.nextLong();
+
+ /**
+ * The dimension to use. This can be any online dimension, or a dimension in the
+ * packs folder
+ */
+ private String dimension = IrisSettings.get().getGenerator().getDefaultWorldType();
+
+ /**
+ * The name of this world.
+ */
+ private String name = "irisworld";
+
+ /**
+ * Headless mode allows Iris to generate / query engine information
+ * without needing an actual world loaded. This is normally only used
+ * for pregeneration purposes but it could be used for mapping.
+ */
+ private boolean headless = false;
+
+ /**
+ * Studio mode makes the engine hotloadable and uses the dimension in
+ * your Iris/packs folder instead of copying the dimension files into
+ * the world itself. Studio worlds are deleted when they are unloaded.
+ */
+ private boolean studio = false;
+
+ /**
+ * Create the IrisAccess (contains the world)
+ * @return the IrisAccess
+ * @throws IrisException shit happens
+ */
+ public IrisAccess create() throws IrisException {
+ IrisDimension d = IrisToolbelt.getDimension(dimension());
+ IrisAccess access = null;
+ Consumer prog = (pxx) -> {
+ double px = (headless && pregen!=null) ? pxx/2 : pxx;
+
+ if(pregen != null && !headless)
+ {
+ px = (px / 2) + 0.5;
+ }
+
+ if(sender != null)
+ {
+ if(sender.isPlayer())
+ {
+ sender.player().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(C.WHITE + "Generating " + Form.pc(px)));
+ }
+
+ else
+ {
+ sender.sendMessage("Generating " + Form.f(px, 0));
+ }
+ }
+ };
+
+ if(d == null)
+ {
+ throw new MissingDimensionException("Cannot find dimension '" + dimension() + "'");
+ }
+
+ if (headless)
+ {
+ HeadlessWorld w = new HeadlessWorld(name, d, seed, studio);
+ access = w.generate().getGenerator();
+ }
+
+ else
+ {
+ O done = new O<>();
+ done.set(false);
+ WorldCreator wc = new IrisWorldCreator()
+ .dimension(dimension)
+ .name(name)
+ .seed(seed)
+ .studio(studio)
+ .create();
+ access = (IrisAccess) wc.generator();
+ IrisAccess finalAccess1 = access;
+
+ J.a(() ->
+ {
+ int req = 400;
+
+ while (finalAccess1.getGenerated() < req && !done.get()) {
+ double v = (double) finalAccess1.getGenerated() / (double) req;
+
+ if(pregen != null)
+ {
+ v /=2;
+ }
+
+ if (sender.isPlayer()) {
+ sender.player().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(C.WHITE + "Generating " + Form.pc(v) + ((C.GRAY + " (" + (req - finalAccess1.getGenerated()) + " Left)"))));
+ J.sleep(50);
+ } else {
+ sender.sendMessage(C.WHITE + "Generating " + Form.pc(v) + ((C.GRAY + " (" + (req - finalAccess1.getGenerated()) + " Left)")));
+ J.sleep(1000);
+ }
+
+ if (finalAccess1.isFailing()) {
+
+ sender.sendMessage("Generation Failed!");
+ break;
+ }
+ }
+
+ sender.player().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(C.WHITE + "Generation Complete"));
+ });
+
+ wc.createWorld();
+ done.set(true);
+ }
+
+ if(access == null)
+ {
+ throw new IrisException("Access is null. Something bad happened.");
+ }
+
+ IrisAccess finalAccess = access;
+ Runnable loadup = () -> {
+ try {
+ J.sfut(() -> {
+ if(headless)
+ {
+ O done = new O<>();
+ done.set(false);
+
+ J.a(() ->
+ {
+ int req = 400;
+
+ while (finalAccess.getGenerated() < req && !done.get()) {
+ double v = (double) finalAccess.getGenerated() / (double) req;
+ v = (v/2) + 0.5;
+
+ if (sender.isPlayer()) {
+ sender.player().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(C.WHITE + "Generating " + Form.pc(v) + ((C.GRAY + " (" + (req - finalAccess.getGenerated()) + " Left)"))));
+ J.sleep(50);
+ } else {
+ sender.sendMessage(C.WHITE + "Generating " + Form.pc(v) + ((C.GRAY + " (" + (req - finalAccess.getGenerated()) + " Left)")));
+ J.sleep(1000);
+ }
+
+ if (finalAccess.isFailing()) {
+
+ sender.sendMessage("Generation Failed!");
+ break;
+ }
+ }
+
+ sender.player().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(C.WHITE + "Generation Complete"));
+ });
+
+ finalAccess.getHeadlessGenerator().getWorld().load();
+ done.set(true);
+ }
+ }).get();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ };
+
+ if(pregen != null)
+ {
+ IrisToolbelt.pregenerate(pregen, access).onProgress(prog).whenDone(loadup);
+ }
+
+ else
+ {
+ loadup.run();
+ }
+
+ return access;
+ }
+}
diff --git a/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java b/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java
new file mode 100644
index 000000000..0ed4da091
--- /dev/null
+++ b/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java
@@ -0,0 +1,166 @@
+/*
+ * 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.tools;
+
+import com.volmit.iris.Iris;
+import com.volmit.iris.core.IrisDataManager;
+import com.volmit.iris.core.gui.PregeneratorJob;
+import com.volmit.iris.core.pregenerator.PregenTask;
+import com.volmit.iris.core.pregenerator.PregeneratorMethod;
+import com.volmit.iris.core.pregenerator.methods.HeadlessPregenMethod;
+import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
+import com.volmit.iris.engine.framework.IrisAccess;
+import com.volmit.iris.engine.object.IrisDimension;
+import com.volmit.iris.util.math.Position2;
+import com.volmit.iris.util.plugin.VolmitSender;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+
+import java.io.File;
+
+/**
+ * Something you really want to wear if working on Iris. Shit gets pretty hectic down there.
+ * Hope you packed snacks & road sodas.
+ */
+public class IrisToolbelt {
+ /**
+ * Will find / download / search for the dimension or return null
+ *
+ * - You can provide a dimenson in the packs folder by the folder name
+ * - You can provide a github repo by using (assumes branch is master unless specified)
+ * - GithubUsername/repository
+ * - GithubUsername/repository/branch
+ *
+ * @param dimension the dimension id such as overworld or flat
+ * @return the IrisDimension or null
+ */
+ public static IrisDimension getDimension(String dimension)
+ {
+ File pack = Iris.instance.getDataFolder("packs", dimension);
+
+ if(!pack.exists())
+ {
+ Iris.proj.downloadSearch(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), dimension, false, false);
+ }
+
+ if(!pack.exists())
+ {
+ return null;
+ }
+
+ return new IrisDataManager(pack).getDimensionLoader().load(dimension);
+ }
+
+ /**
+ * Create a world with plenty of options
+ * @return the creator builder
+ */
+ public static IrisCreator createWorld()
+ {
+ return new IrisCreator();
+ }
+
+ /**
+ * Checks if the given world is an Iris World (same as access(world) != null)
+ * @param world the world
+ * @return true if it is an Iris Access world
+ */
+ public static boolean isIrisWorld(World world)
+ {
+ return access(world) != null;
+ }
+
+ /**
+ * Get the Iris generator for the given world
+ * @param world the given world
+ * @return the IrisAccess or null if it's not an Iris World
+ */
+ public static IrisAccess access(World world)
+ {
+ return IrisWorlds.access(world);
+ }
+
+ /**
+ * Start a pregenerator task
+ * @param task the scheduled task
+ * @param method the method to execute the task
+ * @return the pregenerator job (already started)
+ */
+ public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method)
+ {
+ return new PregeneratorJob(task, method);
+ }
+
+ /**
+ * Start a pregenerator task. If the supplied generator is headless, headless mode is used,
+ * otherwise Hybrid mode is used.
+ * @param task the scheduled task
+ * @param access the Iris Generator
+ * @return the pregenerator job (already started)
+ */
+ public static PregeneratorJob pregenerate(PregenTask task, IrisAccess access)
+ {
+ if(access.isHeadless())
+ {
+ return pregenerate(task, new HeadlessPregenMethod(access.getHeadlessGenerator().getWorld(), access.getHeadlessGenerator()));
+ }
+
+ return pregenerate(task, new HybridPregenMethod(access.getCompound().getWorld().realWorld(), Runtime.getRuntime().availableProcessors()));
+ }
+
+ /**
+ * Evacuate all players from the world into literally any other world.
+ * If there are no other worlds, kick them! Not the best but what's mine is mine sometimes...
+ * @param world the world to evac
+ */
+ public static void evacuate(World world)
+ {
+ IrisWorlds.evacuate(world);
+ }
+
+ public static void test()
+ {
+ // Get IrisDataManager from a world
+ IrisToolbelt.access(anyWorld).getCompound().getData();
+
+ // Get Default Engine from world
+ IrisToolbelt.access(anyWorld).getCompound().getDefaultEngine();
+
+ // Get the engine at the given height
+ IrisToolbelt.access(anyWorld).getCompound().getEngineForHeight(68);
+
+ // IS THIS THING ON?
+ boolean yes = IrisToolbelt.isIrisWorld(world);
+
+ // GTFO for worlds (moves players to any other world, just not this one)
+ IrisToolbelt.evacuate(world);
+
+ IrisAccess access = IrisToolbelt.createWorld() // If you like builders...
+ .name("myWorld") // The world name
+ .dimension("terrifyinghands")
+ .seed(69133742) // The world seed
+ .headless(true) // Headless make gen go fast
+ .pregen(PregenTask // Define a pregen job to run
+ .builder()
+ .center(new Position2(0,0)) // REGION coords (1 region = 32x32 chunks)
+ .radius(4) // Radius in REGIONS. Rad of 4 means a 9x9 Region map.
+ .build())
+ .create();
+ }
+}
diff --git a/src/main/java/com/volmit/iris/engine/object/common/IrisWorld.java b/src/main/java/com/volmit/iris/engine/object/common/IrisWorld.java
index 3d1f5b2c9..6c091baac 100644
--- a/src/main/java/com/volmit/iris/engine/object/common/IrisWorld.java
+++ b/src/main/java/com/volmit/iris/engine/object/common/IrisWorld.java
@@ -19,7 +19,7 @@
package com.volmit.iris.engine.object.common;
import com.volmit.iris.Iris;
-import com.volmit.iris.engine.IrisWorlds;
+import com.volmit.iris.core.tools.IrisWorlds;
import com.volmit.iris.util.collection.KList;
import lombok.Builder;
import lombok.Data;
@@ -27,7 +27,6 @@ import lombok.experimental.Accessors;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
-import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import java.io.File;
diff --git a/src/main/java/com/volmit/iris/util/exceptions/IrisException.java b/src/main/java/com/volmit/iris/util/exceptions/IrisException.java
new file mode 100644
index 000000000..5062f3469
--- /dev/null
+++ b/src/main/java/com/volmit/iris/util/exceptions/IrisException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.util.exceptions;
+
+public class IrisException extends Exception
+{
+ public IrisException() {
+ super();
+ }
+
+ public IrisException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/volmit/iris/util/exceptions/MissingDimensionException.java b/src/main/java/com/volmit/iris/util/exceptions/MissingDimensionException.java
new file mode 100644
index 000000000..f8be9797c
--- /dev/null
+++ b/src/main/java/com/volmit/iris/util/exceptions/MissingDimensionException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.util.exceptions;
+
+public class MissingDimensionException extends IrisException
+{
+ public MissingDimensionException() {
+ super();
+ }
+
+ public MissingDimensionException(String message) {
+ super(message);
+ }
+}