From b081bbb444945b81fd4f40c43a0ff8556f029750 Mon Sep 17 00:00:00 2001 From: Daniel Mills Date: Sun, 8 Aug 2021 07:50:55 -0400 Subject: [PATCH] Engine fixes --- src/main/java/com/volmit/iris/Iris.java | 21 ++- .../com/volmit/iris/engine/IrisEngine.java | 85 ++++++++- .../volmit/iris/engine/framework/Engine.java | 68 ++++--- .../iris/engine/framework/EngineTarget.java | 19 +- .../iris/engine/jigsaw/PlannedPiece.java | 12 +- .../iris/engine/jigsaw/PlannedStructure.java | 8 +- .../iris/engine/object/biome/IrisBiome.java | 4 +- .../engine/object/common/HeadlessWorld.java | 89 +++++++++ .../iris/engine/object/common/IrisWorld.java | 9 +- .../engine/platform/BukkitChunkGenerator.java | 177 ++++++++++++++++++ .../engine/platform/HeadlessGenerator.java | 158 ++++++++++++++++ .../PlatformChunkGenerator.java} | 29 ++- .../iris/util/plugin/MortarCommand.java | 6 + 13 files changed, 623 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/volmit/iris/engine/object/common/HeadlessWorld.java create mode 100644 src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java create mode 100644 src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java rename src/main/java/com/volmit/iris/engine/{framework/IrisAccessProvider.java => platform/PlatformChunkGenerator.java} (54%) diff --git a/src/main/java/com/volmit/iris/Iris.java b/src/main/java/com/volmit/iris/Iris.java index 863eebfcf..8e7a302cb 100644 --- a/src/main/java/com/volmit/iris/Iris.java +++ b/src/main/java/com/volmit/iris/Iris.java @@ -30,9 +30,10 @@ import com.volmit.iris.core.link.OraxenLink; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.framework.EngineCompositeGenerator; +import com.volmit.iris.engine.platform.BukkitChunkGenerator; import com.volmit.iris.engine.object.biome.IrisBiome; import com.volmit.iris.engine.object.biome.IrisBiomeCustom; +import com.volmit.iris.engine.object.common.IrisWorld; import com.volmit.iris.engine.object.compat.IrisCompat; import com.volmit.iris.engine.object.dimensional.IrisDimension; import com.volmit.iris.util.collection.KList; @@ -435,7 +436,23 @@ public class Iris extends VolmitPlugin implements Listener { Iris.info("Generator ID: " + id + " requested by bukkit/plugin. Assuming IrisDimension: " + id); } - return new EngineCompositeGenerator(dimension, true); + IrisDimension d = IrisData.loadAnyDimension(dimension); + + if(d == null) + { + throw new RuntimeException("Can't find dimension " + dimension + "!"); + } + + IrisWorld w = IrisWorld.builder() + .name(worldName) + .seed(RNG.r.lmax()) + .environment(d.getEnvironment()) + .worldFolder(new File(worldName)) + .minHeight(0) + .maxHeight(256) + .build(); + + return new BukkitChunkGenerator(w, false, new File(w.worldFolder(), "iris"), dimension); } public static void msg(String string) { diff --git a/src/main/java/com/volmit/iris/engine/IrisEngine.java b/src/main/java/com/volmit/iris/engine/IrisEngine.java index 0b15a52ed..289517e7a 100644 --- a/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -38,9 +38,14 @@ import com.volmit.iris.engine.object.decoration.IrisDecorator; import com.volmit.iris.engine.object.engine.IrisEngineData; import com.volmit.iris.engine.object.objects.IrisObjectPlacement; import com.volmit.iris.engine.scripting.EngineExecutionEnvironment; +import com.volmit.iris.util.atomics.AtomicRollingSequence; +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.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.math.RNG; @@ -49,10 +54,12 @@ import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Getter; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import org.bukkit.command.CommandSender; import org.bukkit.generator.BlockPopulator; import java.io.File; @@ -63,19 +70,19 @@ import java.util.concurrent.atomic.AtomicBoolean; @EqualsAndHashCode(callSuper = true) @Data public class IrisEngine extends BlockPopulator implements Engine { - private final EngineCompound compound; + // TODO: Remove block population, stop using bukkit private final EngineTarget target; private final IrisContext context; private final EngineEffects effects; private final EngineExecutionEnvironment execution; private final EngineWorldManager worldManager; private volatile int parallelism; - private final int index; private final EngineMetrics metrics; private volatile int minHeight; private boolean failing; private boolean closed; private int cacheId; + private final AtomicRollingSequence wallClock; private final int art; private double maxBiomeObjectDensity; private double maxBiomeLayerDensity; @@ -94,19 +101,19 @@ public class IrisEngine extends BlockPopulator implements Engine { private final AtomicBoolean cleaning; private final ChronoLatch cleanLatch; - public IrisEngine(EngineTarget target, EngineCompound compound, int index) { + public IrisEngine(EngineTarget target) { + wallClock = new AtomicRollingSequence(32); execution = new IrisExecutionEnvironment(this); - Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getHeight() + " height)"); + // TODO: HEIGHT ------------------------------------------------------------------------------------------------------> + Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + 256+ " height)"); metrics = new EngineMetrics(32); this.target = target; getData().setEngine(this); getEngineData(); worldManager = new IrisWorldManager(this); - this.compound = compound; minHeight = 0; failing = false; closed = false; - this.index = index; cacheId = RNG.r.nextInt(); effects = new IrisEngineEffects(this); art = J.ar(effects::tickRandomPlayer, 0); @@ -135,7 +142,8 @@ public class IrisEngine extends BlockPopulator implements Engine { World w = null; return engineData.aquire(() -> { - File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + "-" + getIndex() + ".json"); + //TODO: Method this file + File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + ".json"); if (!f.exists()) { try { @@ -182,6 +190,63 @@ public class IrisEngine extends BlockPopulator implements Engine { } } + + public void printMetrics(CommandSender sender) { + KMap totals = new KMap<>(); + KMap weights = new KMap<>(); + double masterWallClock = wallClock.getAverage(); + KMap timings = getMetrics().pull(); + double totalWeight = 0; + double wallClock = getMetrics().getTotal().getAverage(); + + for (double j : timings.values()) { + totalWeight += j; + } + + for (String j : timings.k()) { + weights.put(getName() + "." + j, (wallClock / totalWeight) * timings.get(j)); + } + + totals.put(getName(), wallClock); + + double mtotals = 0; + + for (double i : totals.values()) { + mtotals += i; + } + + for (String i : totals.k()) { + totals.put(i, (masterWallClock / mtotals) * totals.get(i)); + } + + double v = 0; + + for (double i : weights.values()) { + v += i; + } + + for (String i : weights.k()) { + weights.put(i, weights.get(i) / v); + } + + sender.sendMessage("Total: " + C.BOLD + C.WHITE + Form.duration(masterWallClock, 0)); + + for (String i : totals.k()) { + sender.sendMessage(" Engine " + C.UNDERLINE + C.GREEN + i + C.RESET + ": " + C.BOLD + C.WHITE + Form.duration(totals.get(i), 0)); + } + + sender.sendMessage("Details: "); + + for (String i : weights.sortKNumber().reverse()) { + String befb = C.UNDERLINE + "" + C.GREEN + "" + i.split("\\Q[\\E")[0] + C.RESET + C.GRAY + "["; + String num = C.GOLD + i.split("\\Q[\\E")[1].split("]")[0] + C.RESET + C.GRAY + "]."; + String afb = C.ITALIC + "" + C.AQUA + i.split("\\Q]\\E")[1].substring(1) + C.RESET + C.GRAY; + + sender.sendMessage(" " + befb + num + afb + ": " + C.BOLD + C.WHITE + Form.pc(weights.get(i), 0)); + } + } + + @Override public void close() { J.car(art); @@ -249,7 +314,7 @@ public class IrisEngine extends BlockPopulator implements Engine { return z / getDimension().getTerrainZoom(); } - @ChunkCoordinates + @BlockCoordinates @Override public void generate(int x, int z, Hunk vblocks, Hunk vbiomes, boolean multicore) { context.touch(); @@ -284,7 +349,8 @@ public class IrisEngine extends BlockPopulator implements Engine { @Override public void saveEngineData() { - File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + "-" + getIndex() + ".json"); + //TODO: Method this file + File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + ".json"); f.getParentFile().mkdirs(); try { IO.writeAll(f, new Gson().toJson(getEngineData())); @@ -304,6 +370,7 @@ public class IrisEngine extends BlockPopulator implements Engine { return getData().getBiomeLoader().load(getDimension().getFocus()); } + // TODO: Remove block population @ChunkCoordinates @Override public void populate(World world, Random random, Chunk c) { 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 a42fad916..98a34b2ed 100644 --- a/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -25,6 +25,7 @@ import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.engine.IrisComplex; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.object.basic.IrisColor; +import com.volmit.iris.engine.object.basic.IrisPosition; import com.volmit.iris.engine.object.biome.IrisBiome; import com.volmit.iris.engine.object.common.IrisWorld; import com.volmit.iris.engine.object.dimensional.IrisDimension; @@ -37,6 +38,7 @@ import com.volmit.iris.engine.object.regional.IrisRegion; import com.volmit.iris.engine.parallax.ParallaxAccess; import com.volmit.iris.engine.scripting.EngineExecutionEnvironment; 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.DataProvider; @@ -54,6 +56,7 @@ import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.command.CommandSender; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -61,10 +64,13 @@ import org.bukkit.inventory.ItemStack; import java.awt.*; import java.util.Arrays; import java.util.UUID; +import java.util.function.Consumer; -public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootProvider, BlockUpdater, Renderer, Hotloadable { +public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootProvider, BlockUpdater, Renderer { IrisComplex getComplex(); + void printMetrics(CommandSender sender); + void recycle(); EngineParallaxManager getEngineParallax(); @@ -111,9 +117,10 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro void setMinHeight(int min); - int getIndex(); - - int getMinHeight(); + default int getMinHeight() + { + return getTarget().getWorld().minHeight(); + } @BlockCoordinates double modifyX(double x); @@ -121,7 +128,7 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro @BlockCoordinates double modifyZ(double z); - @ChunkCoordinates + @BlockCoordinates void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore); EngineMetrics getMetrics(); @@ -143,10 +150,6 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro return getDimension().getName(); } - default int getHeight() { - return getTarget().getHeight(); - } - default IrisData getData() { return getTarget().getData(); } @@ -168,7 +171,7 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro IrisRegion region = getRegion((int) x, (int) z); IrisBiome biome = getSurfaceBiome((int) x, (int) z); int height = getHeight((int) x, (int) z); - double heightFactor = M.lerpInverse(0, getHeight(), height); + double heightFactor = M.lerpInverse(0, getTarget().getHeight(), height); Color irc = region.getColor(this.getComplex(), RenderType.BIOME); Color ibc = biome.getColor(this, RenderType.BIOME); Color rc = irc != null ? irc : Color.GREEN.darker(); @@ -404,18 +407,8 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro scramble(inv, rng); } - default int getMaxHeight() { - return getHeight() + getMinHeight(); - } - EngineEffects getEffects(); - EngineCompound getCompound(); - - default boolean isStudio() { - return getCompound().isStudio(); - } - default MultiBurst burst() { return getTarget().getBurster(); } @@ -434,11 +427,6 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro return getRegion(l.getBlockX(), l.getBlockZ()); } - @BlockCoordinates - default boolean contains(Location l) { - return l.getBlockY() >= getMinHeight() && l.getBlockY() <= getMaxHeight(); - } - IrisBiome getFocus(); IrisEngineData getEngineData(); @@ -450,4 +438,34 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro default IrisRegion getRegion(Chunk c) { return getRegion((c.getX() << 4) + 8, (c.getZ() << 4) + 8); } + + default KList getAllBiomes() { + KMap v = new KMap<>(); + + IrisDimension dim = getDimension(); + dim.getAllBiomes(this).forEach((i) -> v.put(i.getLoadKey(), i)); + + try { + dim.getDimensionalComposite().forEach((m) -> getData().getDimensionLoader().load(m.getDimension()).getAllBiomes(this).forEach((i) -> v.put(i.getLoadKey(), i))); + } catch (Throwable ignored) { + Iris.reportError(ignored); + + } + + return v.v(); + } + + int getGenerated(); + + default IrisPosition lookForBiome(IrisBiome biome, int checks, Consumer progress) + { + return null; + } + + default IrisPosition lookForRegion(IrisRegion biome, int checks, Consumer progress) + { + return null; + } + + double getGeneratedPerSecond(); } diff --git a/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java b/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java index 56235322b..ae41fd725 100644 --- a/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java +++ b/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java @@ -24,6 +24,7 @@ import com.volmit.iris.engine.object.common.IrisWorld; import com.volmit.iris.engine.object.dimensional.IrisDimension; import com.volmit.iris.engine.parallax.ParallaxWorld; import com.volmit.iris.util.parallel.MultiBurst; +import lombok.Builder; import lombok.Data; import java.io.File; @@ -34,24 +35,24 @@ public class EngineTarget { private final MultiBurst burster; private final IrisDimension dimension; private IrisWorld world; - private final int height; private final IrisData data; private final ParallaxWorld parallaxWorld; - private final boolean inverted; - public EngineTarget(IrisWorld world, IrisDimension dimension, IrisData data, int height, boolean inverted, int threads) { + public EngineTarget(IrisWorld world, IrisDimension dimension, IrisData data) { this.world = world; - this.height = height; this.dimension = dimension; this.data = data; - this.inverted = inverted; - this.burster = new MultiBurst("Iris Engine " + dimension.getName(), IrisSettings.get().getConcurrency().getEngineThreadPriority(), threads); + this.burster = new MultiBurst("Iris Engine " + dimension.getName(), + IrisSettings.get().getConcurrency().getEngineThreadPriority(), + IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getEngineThreadCount())); this.parallaxBurster = new MultiBurst("Iris Parallax Engine " + dimension.getName(), 3, 4); - this.parallaxWorld = new ParallaxWorld(parallaxBurster, 256, new File(world.worldFolder(), "iris/" + dimension.getLoadKey() + "/parallax")); + this.parallaxWorld = new ParallaxWorld(parallaxBurster, 256, new File(world.worldFolder(), + "iris/" + dimension.getLoadKey() + "/parallax")); } - public EngineTarget(IrisWorld world, IrisDimension dimension, IrisData data, int height, int threads) { - this(world, dimension, data, height, false, threads); + public int getHeight() + { + return world.maxHeight() - world.minHeight(); } public void close() { diff --git a/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index 8587ad090..dbdfca1f4 100644 --- a/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -19,9 +19,8 @@ package com.volmit.iris.engine.jigsaw; import com.volmit.iris.core.project.loader.IrisData; -import com.volmit.iris.core.tools.IrisWorlds; +import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.framework.IrisAccess; import com.volmit.iris.engine.object.basic.IrisPosition; import com.volmit.iris.engine.object.common.IObjectPlacer; import com.volmit.iris.engine.object.jigsaw.IrisJigsawPiece; @@ -32,6 +31,7 @@ import com.volmit.iris.engine.object.objects.IrisObject; import com.volmit.iris.engine.object.objects.IrisObjectRotation; import com.volmit.iris.engine.object.objects.IrisObjectTranslate; import com.volmit.iris.engine.object.tile.TileData; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.math.AxisAlignedBB; import com.volmit.iris.util.math.BlockPosition; @@ -150,13 +150,13 @@ public class PlannedPiece { } public void place(World world) { - IrisAccess a = IrisWorlds.access(world); + PlatformChunkGenerator a = IrisToolbelt.access(world); int minY = 0; if (a != null) { - minY = a.getCompound().getDefaultEngine().getMinHeight(); + minY = a.getEngine().getMinHeight(); - if (!a.getCompound().getRootDimension().isBedrock()) + if (!a.getEngine().getDimension().isBedrock()) minY--; //If the dimension has no bedrock, allow it to go a block lower } @@ -191,7 +191,7 @@ public class PlannedPiece { IrisLootTable table = getPiece().getPlacementOptions().getTable(block.getBlockData(), getData()); if (table == null) return; - Engine engine = a.getCompound().getEngineForHeight(y); + 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); diff --git a/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index aa6698dbe..2ba155748 100644 --- a/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -21,9 +21,9 @@ package com.volmit.iris.engine.jigsaw; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.volmit.iris.Iris; import com.volmit.iris.core.project.loader.IrisData; -import com.volmit.iris.core.tools.IrisWorlds; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineParallaxManager; -import com.volmit.iris.engine.framework.IrisAccess; import com.volmit.iris.engine.object.basic.IrisPosition; import com.volmit.iris.engine.object.common.IObjectPlacer; import com.volmit.iris.engine.object.entity.IrisEntity; @@ -174,7 +174,7 @@ public class PlannedStructure { Iris.sq(() -> { for (IrisJigsawPieceConnector j : i.getAvailableConnectors()) { if (j.getSpawnEntity() != null) { - IrisAccess a = IrisWorlds.access(world); + Engine a = IrisToolbelt.access(world).getEngine(); if (a == null) { Iris.warn("Cannot spawn entities from jigsaw in non Iris world!"); break; @@ -183,7 +183,7 @@ public class PlannedStructure { IrisEntity e = getData().getEntityLoader().load(j.getSpawnEntity()); if (a != null) { - Entity entity = e.spawn(a.getCompound().getEngineForHeight(p.getY()), new Location(world, p.getX() + 0.5, p.getY(), p.getZ() + 0.5), rng); + Entity entity = e.spawn(a, new Location(world, p.getX() + 0.5, p.getY(), p.getZ() + 0.5), rng); if (j.isKeepEntity()) { entity.setPersistent(true); } diff --git a/src/main/java/com/volmit/iris/engine/object/biome/IrisBiome.java b/src/main/java/com/volmit/iris/engine/object/biome/IrisBiome.java index e4a7310c6..fe1c3572e 100644 --- a/src/main/java/com/volmit/iris/engine/object/biome/IrisBiome.java +++ b/src/main/java/com/volmit/iris/engine/object/biome/IrisBiome.java @@ -22,10 +22,10 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.gui.components.RenderType; import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.core.project.loader.IrisRegistrant; +import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.IrisComplex; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.framework.IrisAccess; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.block.IrisBlockDrops; import com.volmit.iris.engine.object.common.IRare; @@ -306,7 +306,7 @@ public class IrisBiome extends IrisRegistrant implements IRare { }); } - public double getHeight(IrisAccess xg, double x, double z, long seed) { + public double getHeight(Engine xg, double x, double z, long seed) { double height = 0; for (IrisBiomeGeneratorLink i : generators) { diff --git a/src/main/java/com/volmit/iris/engine/object/common/HeadlessWorld.java b/src/main/java/com/volmit/iris/engine/object/common/HeadlessWorld.java new file mode 100644 index 000000000..e1dc3f1d3 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/object/common/HeadlessWorld.java @@ -0,0 +1,89 @@ +/* + * 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.engine.object.common; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.project.loader.IrisData; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.platform.HeadlessGenerator; +import com.volmit.iris.engine.platform.BukkitChunkGenerator; +import com.volmit.iris.engine.object.dimensional.IrisDimension; +import com.volmit.iris.util.plugin.VolmitSender; +import lombok.Data; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.WorldCreator; + +import java.io.File; + +@Data +@SuppressWarnings("ResultOfMethodCallIgnored") +public class HeadlessWorld { + private final IrisDimension dimension; + private final String worldName; + private final IrisWorld world; + private boolean studio; + + public HeadlessWorld(String worldName, IrisDimension dimension, long seed) { + this(worldName, dimension, seed, false); + } + + public HeadlessWorld(String worldName, IrisDimension dimension, long seed, boolean studio) { + this.worldName = worldName; + this.dimension = dimension; + this.studio = studio; + world = IrisWorld.builder() + .environment(dimension.getEnvironment()) + .worldFolder(new File(worldName)) + .seed(seed) + .maxHeight(256) + .minHeight(0) + .name(worldName) + .build(); + world.worldFolder().mkdirs(); + new File(world.worldFolder(), "region").mkdirs(); + + if (!studio && !new File(world.worldFolder(), "iris").exists()) { + Iris.proj.installIntoWorld(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag("Headless")), dimension.getLoadKey(), world.worldFolder()); + } + } + + public HeadlessGenerator generate() { + return new HeadlessGenerator(this); + } + + public World load() { + World w = new WorldCreator(worldName) + .environment(dimension.getEnvironment()) + .seed(world.seed()) + .generator(new BukkitChunkGenerator(world, studio, dimension.getLoader().getDataFolder(), + dimension.getLoadKey())) + .createWorld(); + world.realWorld(w); + return w; + } + + public static HeadlessWorld from(World world) { + return new HeadlessWorld(world.getName(), IrisToolbelt.access(world).getEngine().getTarget().getDimension(), world.getSeed()); + } + + public static HeadlessWorld from(String name, String dimension, long seed) { + return new HeadlessWorld(name, IrisData.loadAnyDimension(dimension), seed); + } +} 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 182db04fd..ac4c47d90 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.core.tools.IrisWorlds; +import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.util.collection.KList; import lombok.Builder; import lombok.Data; @@ -76,11 +76,16 @@ public class IrisWorld { public void evacuate() { if (hasRealWorld()) { - IrisWorlds.evacuate(realWorld()); + IrisToolbelt.evacuate(realWorld()); } } public void bind(World world) { + if(hasRealWorld()) + { + return; + } + bindWorld(this, world); } diff --git a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java new file mode 100644 index 000000000..593127c47 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -0,0 +1,177 @@ +/* + * 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.engine.platform; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.project.loader.IrisData; +import com.volmit.iris.engine.IrisEngine; +import com.volmit.iris.engine.data.chunk.TerrainChunk; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.EngineTarget; +import com.volmit.iris.engine.object.common.IrisWorld; +import com.volmit.iris.engine.object.dimensional.IrisDimension; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.io.ReactiveFolder; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator { + private static final BlockData ERROR_BLOCK = Material.RED_GLAZED_TERRACOTTA.createBlockData(); + private final AtomicReference> engine = new AtomicReference<>(); + private final AtomicBoolean busy = new AtomicBoolean(false); + private final IrisWorld world; + private final File dataLocation; + private final String dimensionKey; + private final ReactiveFolder folder; + private final KList populators; + private final boolean studio; + + public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) + { + populators = new KList<>(); + this.world = world; + this.studio = studio; + this.dataLocation = dataLocation; + this.dimensionKey = dimensionKey; + this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> initialize()); + initialize(); + } + + public Engine getEngine() + { + try { + return engine.get().get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + + return null; + } + + @Override + public boolean isHeadless() { + return false; + } + + @Override + public void close() { + getEngine().close(); + } + + + @Override + public void hotload() { + initialize(); + } + + private void initialize() + { + engine.set(MultiBurst.burst.completeValue(() -> createEngine(world, dimensionKey, dataLocation))); + } + + private Engine createEngine(IrisWorld world, String dimension, File dataLocation) { + IrisData data = new IrisData(dataLocation); + IrisDimension realDimension = data.getDimensionLoader().load(dimension); + EngineTarget target = new EngineTarget(world, realDimension, data); + Engine engine = new IrisEngine(target); + populators.clear(); + populators.add((BlockPopulator) engine); + return engine; + } + + @Override + public @NotNull ChunkData generateChunkData(@NotNull World world, @NotNull Random ignored, int x, int z, @NotNull BiomeGrid biome) { + try + { + PrecisionStopwatch ps = PrecisionStopwatch.start(); + TerrainChunk tc = TerrainChunk.create(world, biome); + Hunk blocks = Hunk.view((ChunkData) tc); + Hunk biomes = Hunk.view((BiomeGrid) tc); + this.world.bind(world); + engine.get().get().generate(x * 16, z * 16, blocks, biomes, true); + return tc.getRaw(); + } + + catch(Throwable e) + { + Iris.error("======================================"); + e.printStackTrace(); + Iris.reportErrorChunk(x, z, e, "CHUNK"); + Iris.error("======================================"); + + ChunkData d = Bukkit.createChunkData(world); + + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + d.setBlock(i, 0, j, ERROR_BLOCK); + } + } + + return d; + } + } + + @NotNull + @Override + public List getDefaultPopulators(@NotNull World world) { + return populators; + } + + @Override + public boolean isParallelCapable() { + return true; + } + + @Override + public boolean shouldGenerateCaves() { + return false; + } + + @Override + public boolean shouldGenerateDecorations() { + return false; + } + + @Override + public boolean shouldGenerateMobs() { + return false; + } + + @Override + public boolean shouldGenerateStructures() { + return false; + } +} diff --git a/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java b/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java new file mode 100644 index 000000000..2a6e27083 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java @@ -0,0 +1,158 @@ +/* + * 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.engine.platform; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.pregenerator.PregenListener; +import com.volmit.iris.core.pregenerator.PregenTask; +import com.volmit.iris.engine.IrisEngine; +import com.volmit.iris.engine.data.chunk.MCATerrainChunk; +import com.volmit.iris.engine.data.chunk.TerrainChunk; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.EngineTarget; +import com.volmit.iris.engine.object.common.HeadlessWorld; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.documentation.RegionCoordinates; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.nbt.mca.MCAUtil; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.tag.CompoundTag; +import com.volmit.iris.util.parallel.BurstExecutor; +import com.volmit.iris.util.parallel.MultiBurst; +import lombok.Data; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.generator.ChunkGenerator; + +import java.io.File; +import java.io.IOException; + +@Data +public class HeadlessGenerator implements PlatformChunkGenerator { + private static final BlockData ERROR_BLOCK = Material.RED_GLAZED_TERRACOTTA.createBlockData(); + private static KList EMPTYPOINTS = new KList<>(); + private final HeadlessWorld world; + private final NBTWorld writer; + private final MultiBurst burst; + private final Engine engine; + + public HeadlessGenerator(HeadlessWorld world) { + this.world = world; + burst = new MultiBurst("Iris Headless Generator", 9, IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getPregenThreadCount())); + writer = new NBTWorld(world.getWorld().worldFolder()); + engine = new IrisEngine(new EngineTarget(world.getWorld(),world.getDimension(), world.getDimension().getLoader())); + } + + @ChunkCoordinates + public void generateChunk(int x, int z) { + try { + int ox = x << 4; + int oz = z << 4; + com.volmit.iris.util.nbt.mca.Chunk chunk = writer.getChunk(x, z); + + TerrainChunk tc = MCATerrainChunk.builder() + .writer(writer).ox(ox).oz(oz).mcaChunk(chunk) + .minHeight(world.getWorld().minHeight()).maxHeight(world.getWorld().maxHeight()) + .injector((xx, yy, zz, biomeBase) -> chunk.setBiomeAt(ox + xx, yy, oz + zz, + INMS.get().getTrueBiomeBaseId(biomeBase))) + .build(); + engine.generate(x * 16, z * 16, + Hunk.view((ChunkGenerator.ChunkData) tc), Hunk.view((ChunkGenerator.BiomeGrid) tc), + false); + } catch (Throwable e) { + Iris.error("======================================"); + e.printStackTrace(); + Iris.reportErrorChunk(x, z, e, "MCA"); + Iris.error("======================================"); + com.volmit.iris.util.nbt.mca.Chunk chunk = writer.getChunk(x, z); + CompoundTag c = NBTWorld.getCompound(ERROR_BLOCK); + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + chunk.setBlockStateAt(i, 0, j, c, false); + } + } + } + } + + @RegionCoordinates + public void generateRegion(int x, int z) { + generateRegion(x, z, null); + } + + @RegionCoordinates + public void generateRegion(int x, int z, PregenListener listener) { + BurstExecutor e = burst.burst(1024); + PregenTask.iterateRegion(x, z, (ii, jj) -> e.queue(() -> { + if (listener != null) { + listener.onChunkGenerating(ii, jj); + } + generateChunk(ii, jj); + if (listener != null) { + listener.onChunkGenerated(ii, jj); + } + })); + + e.complete(); + } + + @RegionCoordinates + public File generateRegionToFile(int x, int z, PregenListener listener) { + generateRegion(x, z, listener); + flush(); + return writer.getRegionFile(x, z); + } + + public void flush() { + writer.flushNow(); + } + + public void save() { + writer.save(); + } + + public void close() { + burst.shutdownAndAwait(); + engine.close(); + writer.close(); + } + + public KList getChunksInRegion(int x, int z) { + try { + return MCAUtil.sampleChunkPositions(writer.getRegionFile(x, z)); + } catch (IOException e) { + e.printStackTrace(); + } + + return EMPTYPOINTS; + } + + @Override + public boolean isHeadless() { + return true; + } + + @Override + public void hotload() { + + } +} diff --git a/src/main/java/com/volmit/iris/engine/framework/IrisAccessProvider.java b/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java similarity index 54% rename from src/main/java/com/volmit/iris/engine/framework/IrisAccessProvider.java rename to src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java index 0a1897911..92182243e 100644 --- a/src/main/java/com/volmit/iris/engine/framework/IrisAccessProvider.java +++ b/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java @@ -16,8 +16,31 @@ * along with this program. If not, see . */ -package com.volmit.iris.engine.framework; +package com.volmit.iris.engine.platform; -public interface IrisAccessProvider { - IrisAccess getAccess(); +import com.volmit.iris.core.project.loader.IrisData; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.EngineTarget; +import com.volmit.iris.engine.framework.Hotloadable; +import com.volmit.iris.util.data.DataProvider; + +public interface PlatformChunkGenerator extends Hotloadable, DataProvider { + Engine getEngine(); + + boolean isHeadless(); + + @Override + default IrisData getData() + { + return getEngine().getData(); + } + + default EngineTarget getTarget() + { + return getEngine().getTarget(); + } + + void close(); + + boolean isStudio(); } diff --git a/src/main/java/com/volmit/iris/util/plugin/MortarCommand.java b/src/main/java/com/volmit/iris/util/plugin/MortarCommand.java index 631b58dea..a311b2906 100644 --- a/src/main/java/com/volmit/iris/util/plugin/MortarCommand.java +++ b/src/main/java/com/volmit/iris/util/plugin/MortarCommand.java @@ -18,6 +18,7 @@ package com.volmit.iris.util.plugin; +import com.google.common.collect.Comparators; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.util.collection.KList; @@ -26,6 +27,9 @@ import org.bukkit.Sound; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; /** * Represents a pawn command @@ -197,6 +201,8 @@ public abstract class MortarCommand implements ICommand { } } + p.sort(Comparator.comparing(MortarCommand::getNode)); + return p; }