diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java
index 3586965e7..b6e0fef08 100644
--- a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java
+++ b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java
@@ -1,578 +1,584 @@
-/*
- * Iris is a World Generator for Minecraft Bukkit Servers
- * Copyright (c) 2022 Arcane Arts (Volmit Software)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.volmit.iris.engine;
-
-import com.google.common.util.concurrent.AtomicDouble;
-import com.google.gson.Gson;
-import com.volmit.iris.Iris;
-import com.volmit.iris.core.ServerConfigurator;
-import com.volmit.iris.core.events.IrisEngineHotloadEvent;
-import com.volmit.iris.core.gui.PregeneratorJob;
-import com.volmit.iris.core.loader.ResourceLoader;
-import com.volmit.iris.core.nms.container.BlockPos;
-import com.volmit.iris.core.nms.container.Pair;
-import com.volmit.iris.core.project.IrisProject;
-import com.volmit.iris.core.scripting.environment.EngineEnvironment;
-import com.volmit.iris.core.service.PreservationSVC;
-import com.volmit.iris.engine.data.cache.AtomicCache;
-import com.volmit.iris.engine.framework.*;
-import com.volmit.iris.engine.mantle.EngineMantle;
-import com.volmit.iris.engine.object.*;
-import com.volmit.iris.util.atomics.AtomicRollingSequence;
-import com.volmit.iris.util.collection.KMap;
-import com.volmit.iris.util.context.ChunkContext;
-import com.volmit.iris.util.context.IrisContext;
-import com.volmit.iris.util.documentation.BlockCoordinates;
-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.mantle.flag.MantleFlag;
-import com.volmit.iris.util.math.M;
-import com.volmit.iris.util.math.RNG;
-import com.volmit.iris.util.matter.MatterStructurePOI;
-import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer;
-import com.volmit.iris.util.scheduling.ChronoLatch;
-import com.volmit.iris.util.scheduling.J;
-import com.volmit.iris.util.scheduling.PrecisionStopwatch;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-import org.bukkit.Material;
-import org.bukkit.block.Biome;
-import org.bukkit.block.data.BlockData;
-import org.bukkit.command.CommandSender;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-@Data
-@EqualsAndHashCode(exclude = "context")
-@ToString(exclude = "context")
-public class IrisEngine implements Engine {
- private final AtomicInteger bud;
- private final AtomicInteger buds;
- private final AtomicInteger generated;
- private final AtomicInteger generatedLast;
- private final AtomicDouble perSecond;
- private final AtomicLong lastGPS;
- private final EngineTarget target;
- private final IrisContext context;
- private final EngineMantle mantle;
- private final ChronoLatch perSecondLatch;
- private final ChronoLatch perSecondBudLatch;
- private final EngineMetrics metrics;
- private final boolean studio;
- private final AtomicRollingSequence wallClock;
- private final int art;
- private final AtomicCache engineData = new AtomicCache<>();
- private final AtomicBoolean cleaning;
- private final ChronoLatch cleanLatch;
- private final SeedManager seedManager;
- private CompletableFuture hash32;
- private EngineMode mode;
- private EngineEffects effects;
- private EngineEnvironment execution;
- private EngineWorldManager worldManager;
- private volatile int parallelism;
- private boolean failing;
- private boolean closed;
- private int cacheId;
- private double maxBiomeObjectDensity;
- private double maxBiomeLayerDensity;
- private double maxBiomeDecoratorDensity;
- private IrisComplex complex;
-
- public IrisEngine(EngineTarget target, boolean studio) {
- this.studio = studio;
- this.target = target;
- getEngineData();
- verifySeed();
- this.seedManager = new SeedManager(target.getWorld().getRawWorldSeed());
- bud = new AtomicInteger(0);
- buds = new AtomicInteger(0);
- metrics = new EngineMetrics(32);
- cleanLatch = new ChronoLatch(10000);
- generatedLast = new AtomicInteger(0);
- perSecond = new AtomicDouble(0);
- perSecondLatch = new ChronoLatch(1000, false);
- perSecondBudLatch = new ChronoLatch(1000, false);
- wallClock = new AtomicRollingSequence(32);
- lastGPS = new AtomicLong(M.ms());
- generated = new AtomicInteger(0);
- mantle = new IrisEngineMantle(this);
- context = new IrisContext(this);
- cleaning = new AtomicBoolean(false);
- context.touch();
- getData().setEngine(this);
- getData().loadPrefetch(this);
- Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed());
- failing = false;
- closed = false;
- art = J.ar(this::tickRandomPlayer, 0);
- setupEngine();
- Iris.debug("Engine Initialized " + getCacheID());
- }
-
- private void verifySeed() {
- if (getEngineData().getSeed() != null && getEngineData().getSeed() != target.getWorld().getRawWorldSeed()) {
- target.getWorld().setRawWorldSeed(getEngineData().getSeed());
- }
- }
-
- private void tickRandomPlayer() {
- recycle();
- if (perSecondBudLatch.flip()) {
- buds.set(bud.get());
- bud.set(0);
- }
-
- if (effects != null) {
- effects.tickRandomPlayer();
- }
- }
-
- private void prehotload() {
- worldManager.close();
- complex.close();
- execution.close();
- effects.close();
- mode.close();
-
- J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
- }
-
- private void setupEngine() {
- try {
- Iris.debug("Setup Engine " + getCacheID());
- cacheId = RNG.r.nextInt();
- worldManager = new IrisWorldManager(this);
- complex = new IrisComplex(this);
- execution = EngineEnvironment.create(this);
- effects = new IrisEngineEffects(this);
- hash32 = new CompletableFuture<>();
- mantle.hotload();
- setupMode();
- getDimension().getEngineScripts().forEach(execution::execute);
- J.a(this::computeBiomeMaxes);
- J.a(() -> {
- File[] roots = getData().getLoaders()
- .values()
- .stream()
- .map(ResourceLoader::getFolderName)
- .map(n -> new File(getData().getDataFolder(), n))
- .filter(File::exists)
- .filter(File::isDirectory)
- .toArray(File[]::new);
- hash32.complete(IO.hashRecursive(roots));
- });
- } catch (Throwable e) {
- Iris.error("FAILED TO SETUP ENGINE!");
- e.printStackTrace();
- }
-
- Iris.debug("Engine Setup Complete " + getCacheID());
- }
-
- private void setupMode() {
- if (mode != null) {
- mode.close();
- }
-
- mode = getDimension().getMode().create(this);
- }
-
- @Override
- public void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
- getMantle().generateMatter(x, z, multicore, context);
- }
-
- @Override
- public Set getObjectsAt(int x, int z) {
- return getMantle().getObjectComponent().guess(x, z);
- }
-
- @Override
- public Set> getPOIsAt(int chunkX, int chunkY) {
- Set> pois = new HashSet<>();
- getMantle().getMantle().iterateChunk(chunkX, chunkY, MatterStructurePOI.class, (x, y, z, d) -> pois.add(new Pair<>(d.getType(), new BlockPos(x, y, z))));
- return pois;
- }
-
- @Override
- public IrisJigsawStructure getStructureAt(int x, int z) {
- return getMantle().getJigsawComponent().guess(x, z);
- }
-
- @Override
- public IrisJigsawStructure getStructureAt(int x, int y, int z) {
- var container = getMantle().getMantle().get(x, y, z, JigsawStructureContainer.class);
- return container == null ? null : container.load(getData());
- }
-
- private void warmupChunk(int x, int z) {
- for (int i = 0; i < 16; i++) {
- for (int j = 0; j < 16; j++) {
- int xx = x + (i << 4);
- int zz = z + (z << 4);
- getComplex().getTrueBiomeStream().get(xx, zz);
- getComplex().getHeightStream().get(xx, zz);
- }
- }
- }
-
- @Override
- public void hotload() {
- hotloadSilently();
- Iris.callEvent(new IrisEngineHotloadEvent(this));
- }
-
- public void hotloadComplex() {
- complex.close();
- complex = new IrisComplex(this);
- }
-
- public void hotloadSilently() {
- getData().dump();
- getData().clearLists();
- getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey()));
- prehotload();
- setupEngine();
- J.a(() -> {
- synchronized (ServerConfigurator.class) {
- ServerConfigurator.installDataPacks(false);
- }
- });
- }
-
- @Override
- public IrisEngineData getEngineData() {
- return engineData.aquire(() -> {
- //TODO: Method this file
- File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + ".json");
- IrisEngineData data = null;
-
- if (f.exists()) {
- try {
- data = new Gson().fromJson(IO.readAll(f), IrisEngineData.class);
- if (data == null) {
- Iris.error("Failed to read Engine Data! Corrupted File? recreating...");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- if (data == null) {
- data = new IrisEngineData();
- data.getStatistics().setVersion(Iris.instance.getIrisVersion());
- data.getStatistics().setMCVersion(Iris.instance.getMCVersion());
- data.getStatistics().setUpgradedVersion(Iris.instance.getIrisVersion());
- if (data.getStatistics().getVersion() == -1 || data.getStatistics().getMCVersion() == -1 ) {
- Iris.error("Failed to setup Engine Data!");
- }
-
- if (f.getParentFile().exists() || f.getParentFile().mkdirs()) {
- try {
- IO.writeAll(f, new Gson().toJson(data));
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else {
- Iris.error("Failed to setup Engine Data!");
- }
- }
-
- return data;
- });
- }
-
- @Override
- public int getGenerated() {
- return generated.get();
- }
-
- @Override
- public double getGeneratedPerSecond() {
- if (perSecondLatch.flip()) {
- double g = generated.get() - generatedLast.get();
- generatedLast.set(generated.get());
-
- if (g == 0) {
- return 0;
- }
-
- long dur = M.ms() - lastGPS.get();
- lastGPS.set(M.ms());
- perSecond.set(g / ((double) (dur) / 1000D));
- }
-
- return perSecond.get();
- }
-
- @Override
- public boolean isStudio() {
- return studio;
- }
-
- private void computeBiomeMaxes() {
- for (IrisBiome i : getDimension().getAllBiomes(this)) {
- double density = 0;
-
- for (IrisObjectPlacement j : i.getObjects()) {
- density += j.getDensity() * j.getChance();
- }
-
- maxBiomeObjectDensity = Math.max(maxBiomeObjectDensity, density);
- density = 0;
-
- for (IrisDecorator j : i.getDecorators()) {
- density += Math.max(j.getStackMax(), 1) * j.getChance();
- }
-
- maxBiomeDecoratorDensity = Math.max(maxBiomeDecoratorDensity, density);
- density = 0;
-
- for (IrisBiomePaletteLayer j : i.getLayers()) {
- density++;
- }
-
- maxBiomeLayerDensity = Math.max(maxBiomeLayerDensity, density);
- }
- }
-
- @Override
- public int getBlockUpdatesPerSecond() {
- return buds.get();
- }
-
- 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() {
- PregeneratorJob.shutdownInstance();
- closed = true;
- J.car(art);
- getWorldManager().close();
- getTarget().close();
- saveEngineData();
- getMantle().close();
- getComplex().close();
- mode.close();
- getData().dump();
- getData().clearLists();
- Iris.service(PreservationSVC.class).dereference();
- Iris.debug("Engine Fully Shutdown!");
- complex = null;
- }
-
- @Override
- public boolean isClosed() {
- return closed;
- }
-
- @Override
- public void recycle() {
- if (!cleanLatch.flip()) {
- return;
- }
-
- if (cleaning.get()) {
- cleanLatch.flipDown();
- return;
- }
-
- cleaning.set(true);
-
- J.a(() -> {
- try {
- getData().getObjectLoader().clean();
- } catch (Throwable e) {
- Iris.reportError(e);
- Iris.error("Cleanup failed! Enable debug to see stacktrace.");
- }
-
- cleaning.lazySet(false);
- });
- }
-
- @BlockCoordinates
- @Override
- public void generate(int x, int z, Hunk vblocks, Hunk vbiomes, boolean multicore) throws WrongEngineBroException {
- if (closed) {
- throw new WrongEngineBroException();
- }
-
- context.touch();
- getEngineData().getStatistics().generatedChunk();
- try {
- PrecisionStopwatch p = PrecisionStopwatch.start();
- Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
-
- if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
- for (int i = 0; i < 16; i++) {
- for (int j = 0; j < 16; j++) {
- blocks.set(i, 0, j, Material.CRYING_OBSIDIAN.createBlockData());
- }
- }
- } else {
- mode.generate(x, z, blocks, vbiomes, multicore);
- }
-
- getMantle().getMantle().flag(x >> 4, z >> 4, MantleFlag.REAL, true);
- getMetrics().getTotal().put(p.getMilliseconds());
- generated.incrementAndGet();
-
- if (generated.get() == 661) {
- J.a(() -> getData().savePrefetch(this));
- }
- } catch (Throwable e) {
- Iris.reportError(e);
- fail("Failed to generate " + x + ", " + z, e);
- }
- }
-
- @Override
- public void saveEngineData() {
- //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()));
- Iris.debug("Saved Engine Data");
- } catch (IOException e) {
- Iris.error("Failed to save Engine Data");
- e.printStackTrace();
- }
- }
-
- @Override
- public void blockUpdatedMetric() {
- bud.incrementAndGet();
- }
-
- @Override
- public IrisBiome getFocus() {
- if (getDimension().getFocus() == null || getDimension().getFocus().trim().isEmpty()) {
- return null;
- }
-
- return getData().getBiomeLoader().load(getDimension().getFocus());
- }
-
- @Override
- public IrisRegion getFocusRegion() {
- if (getDimension().getFocusRegion() == null || getDimension().getFocusRegion().trim().isEmpty()) {
- return null;
- }
-
- return getData().getRegionLoader().load(getDimension().getFocusRegion());
- }
-
- @Override
- public void fail(String error, Throwable e) {
- failing = true;
- Iris.error(error);
- e.printStackTrace();
- }
-
- @Override
- public boolean hasFailed() {
- return failing;
- }
-
- @Override
- public int getCacheID() {
- return cacheId;
- }
-
- private boolean EngineSafe() {
- // Todo: this has potential if done right
- int EngineMCVersion = getEngineData().getStatistics().getMCVersion();
- int EngineIrisVersion = getEngineData().getStatistics().getVersion();
- int MinecraftVersion = Iris.instance.getMCVersion();
- int IrisVersion = Iris.instance.getIrisVersion();
- if (EngineIrisVersion != IrisVersion) {
- return false;
- }
- if (EngineMCVersion != MinecraftVersion) {
- return false;
- }
- return true;
- }
-}
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.volmit.iris.engine;
+
+import com.google.common.util.concurrent.AtomicDouble;
+import com.google.gson.Gson;
+import com.volmit.iris.Iris;
+import com.volmit.iris.core.ServerConfigurator;
+import com.volmit.iris.core.events.IrisEngineHotloadEvent;
+import com.volmit.iris.core.gui.PregeneratorJob;
+import com.volmit.iris.core.loader.ResourceLoader;
+import com.volmit.iris.core.nms.container.BlockPos;
+import com.volmit.iris.core.nms.container.Pair;
+import com.volmit.iris.core.project.IrisProject;
+import com.volmit.iris.core.scripting.environment.EngineEnvironment;
+import com.volmit.iris.core.service.PreservationSVC;
+import com.volmit.iris.engine.data.cache.AtomicCache;
+import com.volmit.iris.engine.framework.*;
+import com.volmit.iris.engine.mantle.EngineMantle;
+import com.volmit.iris.engine.object.*;
+import com.volmit.iris.util.atomics.AtomicRollingSequence;
+import com.volmit.iris.util.collection.KMap;
+import com.volmit.iris.util.context.ChunkContext;
+import com.volmit.iris.util.context.IrisContext;
+import com.volmit.iris.util.documentation.BlockCoordinates;
+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.mantle.flag.MantleFlag;
+import com.volmit.iris.util.math.M;
+import com.volmit.iris.util.math.RNG;
+import com.volmit.iris.util.matter.MatterStructurePOI;
+import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer;
+import com.volmit.iris.util.scheduling.ChronoLatch;
+import com.volmit.iris.util.scheduling.J;
+import com.volmit.iris.util.scheduling.PrecisionStopwatch;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.bukkit.Material;
+import org.bukkit.block.Biome;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.command.CommandSender;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Data
+@EqualsAndHashCode(exclude = "context")
+@ToString(exclude = "context")
+public class IrisEngine implements Engine {
+ private final AtomicInteger bud;
+ private final AtomicInteger buds;
+ private final AtomicInteger generated;
+ private final AtomicInteger generatedLast;
+ private final AtomicDouble perSecond;
+ private final AtomicLong lastGPS;
+ private final EngineTarget target;
+ private final IrisContext context;
+ private final EngineMantle mantle;
+ private final ChronoLatch perSecondLatch;
+ private final ChronoLatch perSecondBudLatch;
+ private final EngineMetrics metrics;
+ private final boolean studio;
+ private final AtomicRollingSequence wallClock;
+ private final int art;
+ private final AtomicCache engineData = new AtomicCache<>();
+ private final AtomicBoolean cleaning;
+ private final ChronoLatch cleanLatch;
+ private final SeedManager seedManager;
+ private CompletableFuture hash32;
+ private EngineMode mode;
+ private EngineEffects effects;
+ private EngineEnvironment execution;
+ private EngineWorldManager worldManager;
+ private volatile int parallelism;
+ private boolean failing;
+ private boolean closed;
+ private int cacheId;
+ private double maxBiomeObjectDensity;
+ private double maxBiomeLayerDensity;
+ private double maxBiomeDecoratorDensity;
+ private IrisComplex complex;
+
+ public IrisEngine(EngineTarget target, boolean studio) {
+ this.studio = studio;
+ this.target = target;
+ getEngineData();
+ verifySeed();
+ this.seedManager = new SeedManager(target.getWorld().getRawWorldSeed());
+ bud = new AtomicInteger(0);
+ buds = new AtomicInteger(0);
+ metrics = new EngineMetrics(32);
+ cleanLatch = new ChronoLatch(10000);
+ generatedLast = new AtomicInteger(0);
+ perSecond = new AtomicDouble(0);
+ perSecondLatch = new ChronoLatch(1000, false);
+ perSecondBudLatch = new ChronoLatch(1000, false);
+ wallClock = new AtomicRollingSequence(32);
+ lastGPS = new AtomicLong(M.ms());
+ generated = new AtomicInteger(0);
+ mantle = new IrisEngineMantle(this);
+ context = new IrisContext(this);
+ cleaning = new AtomicBoolean(false);
+ execution = EngineEnvironment.create(this);
+ if (studio) {
+ getData().dump();
+ getData().clearLists();
+ getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey()));
+ }
+ context.touch();
+ getData().setEngine(this);
+ getData().loadPrefetch(this);
+ Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed());
+ failing = false;
+ closed = false;
+ art = J.ar(this::tickRandomPlayer, 0);
+ setupEngine();
+ Iris.debug("Engine Initialized " + getCacheID());
+ }
+
+ private void verifySeed() {
+ if (getEngineData().getSeed() != null && getEngineData().getSeed() != target.getWorld().getRawWorldSeed()) {
+ target.getWorld().setRawWorldSeed(getEngineData().getSeed());
+ }
+ }
+
+ private void tickRandomPlayer() {
+ recycle();
+ if (perSecondBudLatch.flip()) {
+ buds.set(bud.get());
+ bud.set(0);
+ }
+
+ if (effects != null) {
+ effects.tickRandomPlayer();
+ }
+ }
+
+ private void prehotload() {
+ worldManager.close();
+ complex.close();
+ execution.close();
+ effects.close();
+ mode.close();
+ execution = EngineEnvironment.create(this);
+
+ J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
+ }
+
+ private void setupEngine() {
+ try {
+ Iris.debug("Setup Engine " + getCacheID());
+ cacheId = RNG.r.nextInt();
+ worldManager = new IrisWorldManager(this);
+ complex = new IrisComplex(this);
+ effects = new IrisEngineEffects(this);
+ hash32 = new CompletableFuture<>();
+ mantle.hotload();
+ setupMode();
+ getDimension().getEngineScripts().forEach(execution::execute);
+ J.a(this::computeBiomeMaxes);
+ J.a(() -> {
+ File[] roots = getData().getLoaders()
+ .values()
+ .stream()
+ .map(ResourceLoader::getFolderName)
+ .map(n -> new File(getData().getDataFolder(), n))
+ .filter(File::exists)
+ .filter(File::isDirectory)
+ .toArray(File[]::new);
+ hash32.complete(IO.hashRecursive(roots));
+ });
+ } catch (Throwable e) {
+ Iris.error("FAILED TO SETUP ENGINE!");
+ e.printStackTrace();
+ }
+
+ Iris.debug("Engine Setup Complete " + getCacheID());
+ }
+
+ private void setupMode() {
+ if (mode != null) {
+ mode.close();
+ }
+
+ mode = getDimension().getMode().create(this);
+ }
+
+ @Override
+ public void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
+ getMantle().generateMatter(x, z, multicore, context);
+ }
+
+ @Override
+ public Set getObjectsAt(int x, int z) {
+ return getMantle().getObjectComponent().guess(x, z);
+ }
+
+ @Override
+ public Set> getPOIsAt(int chunkX, int chunkY) {
+ Set> pois = new HashSet<>();
+ getMantle().getMantle().iterateChunk(chunkX, chunkY, MatterStructurePOI.class, (x, y, z, d) -> pois.add(new Pair<>(d.getType(), new BlockPos(x, y, z))));
+ return pois;
+ }
+
+ @Override
+ public IrisJigsawStructure getStructureAt(int x, int z) {
+ return getMantle().getJigsawComponent().guess(x, z);
+ }
+
+ @Override
+ public IrisJigsawStructure getStructureAt(int x, int y, int z) {
+ var container = getMantle().getMantle().get(x, y, z, JigsawStructureContainer.class);
+ return container == null ? null : container.load(getData());
+ }
+
+ private void warmupChunk(int x, int z) {
+ for (int i = 0; i < 16; i++) {
+ for (int j = 0; j < 16; j++) {
+ int xx = x + (i << 4);
+ int zz = z + (z << 4);
+ getComplex().getTrueBiomeStream().get(xx, zz);
+ getComplex().getHeightStream().get(xx, zz);
+ }
+ }
+ }
+
+ @Override
+ public void hotload() {
+ hotloadSilently();
+ Iris.callEvent(new IrisEngineHotloadEvent(this));
+ }
+
+ public void hotloadComplex() {
+ complex.close();
+ complex = new IrisComplex(this);
+ }
+
+ public void hotloadSilently() {
+ getData().dump();
+ getData().clearLists();
+ getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey()));
+ prehotload();
+ setupEngine();
+ J.a(() -> {
+ synchronized (ServerConfigurator.class) {
+ ServerConfigurator.installDataPacks(false);
+ }
+ });
+ }
+
+ @Override
+ public IrisEngineData getEngineData() {
+ return engineData.aquire(() -> {
+ //TODO: Method this file
+ File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + ".json");
+ IrisEngineData data = null;
+
+ if (f.exists()) {
+ try {
+ data = new Gson().fromJson(IO.readAll(f), IrisEngineData.class);
+ if (data == null) {
+ Iris.error("Failed to read Engine Data! Corrupted File? recreating...");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (data == null) {
+ data = new IrisEngineData();
+ data.getStatistics().setVersion(Iris.instance.getIrisVersion());
+ data.getStatistics().setMCVersion(Iris.instance.getMCVersion());
+ data.getStatistics().setUpgradedVersion(Iris.instance.getIrisVersion());
+ if (data.getStatistics().getVersion() == -1 || data.getStatistics().getMCVersion() == -1 ) {
+ Iris.error("Failed to setup Engine Data!");
+ }
+
+ if (f.getParentFile().exists() || f.getParentFile().mkdirs()) {
+ try {
+ IO.writeAll(f, new Gson().toJson(data));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ Iris.error("Failed to setup Engine Data!");
+ }
+ }
+
+ return data;
+ });
+ }
+
+ @Override
+ public int getGenerated() {
+ return generated.get();
+ }
+
+ @Override
+ public double getGeneratedPerSecond() {
+ if (perSecondLatch.flip()) {
+ double g = generated.get() - generatedLast.get();
+ generatedLast.set(generated.get());
+
+ if (g == 0) {
+ return 0;
+ }
+
+ long dur = M.ms() - lastGPS.get();
+ lastGPS.set(M.ms());
+ perSecond.set(g / ((double) (dur) / 1000D));
+ }
+
+ return perSecond.get();
+ }
+
+ @Override
+ public boolean isStudio() {
+ return studio;
+ }
+
+ private void computeBiomeMaxes() {
+ for (IrisBiome i : getDimension().getAllBiomes(this)) {
+ double density = 0;
+
+ for (IrisObjectPlacement j : i.getObjects()) {
+ density += j.getDensity() * j.getChance();
+ }
+
+ maxBiomeObjectDensity = Math.max(maxBiomeObjectDensity, density);
+ density = 0;
+
+ for (IrisDecorator j : i.getDecorators()) {
+ density += Math.max(j.getStackMax(), 1) * j.getChance();
+ }
+
+ maxBiomeDecoratorDensity = Math.max(maxBiomeDecoratorDensity, density);
+ density = 0;
+
+ for (IrisBiomePaletteLayer j : i.getLayers()) {
+ density++;
+ }
+
+ maxBiomeLayerDensity = Math.max(maxBiomeLayerDensity, density);
+ }
+ }
+
+ @Override
+ public int getBlockUpdatesPerSecond() {
+ return buds.get();
+ }
+
+ 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() {
+ PregeneratorJob.shutdownInstance();
+ closed = true;
+ J.car(art);
+ getWorldManager().close();
+ getTarget().close();
+ saveEngineData();
+ getMantle().close();
+ getComplex().close();
+ mode.close();
+ getData().dump();
+ getData().clearLists();
+ Iris.service(PreservationSVC.class).dereference();
+ Iris.debug("Engine Fully Shutdown!");
+ complex = null;
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public void recycle() {
+ if (!cleanLatch.flip()) {
+ return;
+ }
+
+ if (cleaning.get()) {
+ cleanLatch.flipDown();
+ return;
+ }
+
+ cleaning.set(true);
+
+ J.a(() -> {
+ try {
+ getData().getObjectLoader().clean();
+ } catch (Throwable e) {
+ Iris.reportError(e);
+ Iris.error("Cleanup failed! Enable debug to see stacktrace.");
+ }
+
+ cleaning.lazySet(false);
+ });
+ }
+
+ @BlockCoordinates
+ @Override
+ public void generate(int x, int z, Hunk vblocks, Hunk vbiomes, boolean multicore) throws WrongEngineBroException {
+ if (closed) {
+ throw new WrongEngineBroException();
+ }
+
+ context.touch();
+ getEngineData().getStatistics().generatedChunk();
+ try {
+ PrecisionStopwatch p = PrecisionStopwatch.start();
+ Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
+
+ if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
+ for (int i = 0; i < 16; i++) {
+ for (int j = 0; j < 16; j++) {
+ blocks.set(i, 0, j, Material.CRYING_OBSIDIAN.createBlockData());
+ }
+ }
+ } else {
+ mode.generate(x, z, blocks, vbiomes, multicore);
+ }
+
+ getMantle().getMantle().flag(x >> 4, z >> 4, MantleFlag.REAL, true);
+ getMetrics().getTotal().put(p.getMilliseconds());
+ generated.incrementAndGet();
+
+ if (generated.get() == 661) {
+ J.a(() -> getData().savePrefetch(this));
+ }
+ } catch (Throwable e) {
+ Iris.reportError(e);
+ fail("Failed to generate " + x + ", " + z, e);
+ }
+ }
+
+ @Override
+ public void saveEngineData() {
+ //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()));
+ Iris.debug("Saved Engine Data");
+ } catch (IOException e) {
+ Iris.error("Failed to save Engine Data");
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void blockUpdatedMetric() {
+ bud.incrementAndGet();
+ }
+
+ @Override
+ public IrisBiome getFocus() {
+ if (getDimension().getFocus() == null || getDimension().getFocus().trim().isEmpty()) {
+ return null;
+ }
+
+ return getData().getBiomeLoader().load(getDimension().getFocus());
+ }
+
+ @Override
+ public IrisRegion getFocusRegion() {
+ if (getDimension().getFocusRegion() == null || getDimension().getFocusRegion().trim().isEmpty()) {
+ return null;
+ }
+
+ return getData().getRegionLoader().load(getDimension().getFocusRegion());
+ }
+
+ @Override
+ public void fail(String error, Throwable e) {
+ failing = true;
+ Iris.error(error);
+ e.printStackTrace();
+ }
+
+ @Override
+ public boolean hasFailed() {
+ return failing;
+ }
+
+ @Override
+ public int getCacheID() {
+ return cacheId;
+ }
+
+ private boolean EngineSafe() {
+ // Todo: this has potential if done right
+ int EngineMCVersion = getEngineData().getStatistics().getMCVersion();
+ int EngineIrisVersion = getEngineData().getStatistics().getVersion();
+ int MinecraftVersion = Iris.instance.getMCVersion();
+ int IrisVersion = Iris.instance.getIrisVersion();
+ if (EngineIrisVersion != IrisVersion) {
+ return false;
+ }
+ if (EngineMCVersion != MinecraftVersion) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java
index c16bebc3a..fe991f753 100644
--- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java
+++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java
@@ -166,6 +166,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
return targetCache.aquire(() -> {
IrisData data = IrisData.get(dataLocation);
+ data.dump();
+ data.clearLists();
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) {
diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/EngineScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/EngineScript.kt
index 768f2a9d3..8a091a50f 100644
--- a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/EngineScript.kt
+++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/EngineScript.kt
@@ -14,9 +14,9 @@ abstract class EngineScript
object EngineScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
providedProperties(
"engine" to Engine::class,
- "complex" to IrisComplex::class,
"seed" to Long::class,
"dimension" to IrisDimension::class,
+ "complex" to IrisComplex::class,
"biome" to BiomeLookup::class,
)
}) {
diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PreprocessorScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PreprocessorScript.kt
index f594649e1..c8941bb8d 100644
--- a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PreprocessorScript.kt
+++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PreprocessorScript.kt
@@ -1,6 +1,8 @@
package com.volmit.iris.core.scripting.kotlin.base
import com.volmit.iris.core.loader.IrisRegistrant
+import com.volmit.iris.engine.framework.Engine
+import com.volmit.iris.engine.`object`.IrisDimension
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.providedProperties
@@ -8,8 +10,13 @@ import kotlin.script.experimental.api.providedProperties
@KotlinScript(fileExtension = "proc.kts", compilationConfiguration = PreprocessorScriptDefinition::class)
abstract class PreprocessorScript
-object PreprocessorScriptDefinition : ScriptCompilationConfiguration(listOf(EngineScriptDefinition), {
- providedProperties("object" to IrisRegistrant::class)
+object PreprocessorScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
+ providedProperties(
+ "engine" to Engine::class,
+ "seed" to Long::class,
+ "dimension" to IrisDimension::class,
+ "object" to IrisRegistrant::class
+ )
}) {
private fun readResolve(): Any = PreprocessorScriptDefinition
}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisExecutionEnvironment.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisExecutionEnvironment.kt
index fc80c7738..2bebf8c9f 100644
--- a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisExecutionEnvironment.kt
+++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisExecutionEnvironment.kt
@@ -33,18 +33,24 @@ data class IrisExecutionEnvironment(
execute(script, PostMobSpawningScript::class.java, engine.parameters("location" to location, "entity" to mob))
override fun preprocessObject(script: String, `object`: IrisRegistrant) =
- execute(script, PreprocessorScript::class.java, engine.parameters("object" to `object`))
+ execute(script, PreprocessorScript::class.java, engine.limitedParameters("object" to `object`))
override fun updateChunk(script: String, mantleChunk: MantleChunk, chunk: Chunk, executor: UpdateExecutor) =
execute(script, ChunkUpdateScript::class.java, engine.parameters("mantleChunk" to mantleChunk, "chunk" to chunk, "executor" to executor))
- private fun Engine.parameters(vararg values: Pair): Map {
+ private fun Engine.limitedParameters(vararg values: Pair): Map {
return mapOf(
"data" to data,
"engine" to this,
- "complex" to complex,
"seed" to seedManager.seed,
"dimension" to dimension,
+ *values,
+ )
+ }
+
+ private fun Engine.parameters(vararg values: Pair): Map {
+ return limitedParameters(
+ "complex" to complex,
"biome" to BiomeLookup(::getSurfaceBiome),
*values,
)
diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisSimpleExecutionEnvironment.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisSimpleExecutionEnvironment.kt
index 2896b5821..9987a0e01 100644
--- a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisSimpleExecutionEnvironment.kt
+++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisSimpleExecutionEnvironment.kt
@@ -7,7 +7,7 @@ import com.volmit.iris.core.scripting.kotlin.base.*
import com.volmit.iris.core.scripting.kotlin.runner.Script
import com.volmit.iris.core.scripting.kotlin.runner.ScriptRunner
import com.volmit.iris.core.scripting.kotlin.runner.classpath
-import com.volmit.iris.core.scripting.kotlin.runner.valueOrNull
+import com.volmit.iris.core.scripting.kotlin.runner.value
import com.volmit.iris.core.scripting.kotlin.runner.valueOrThrow
import com.volmit.iris.util.collection.KMap
import com.volmit.iris.util.data.KCache
@@ -68,7 +68,7 @@ open class IrisSimpleExecutionEnvironment(
return compile(name, type)
.evaluate(properties)
.valueOrThrow("Failed to evaluate script")
- .valueOrNull()
+ .value()
} catch (e: Throwable) {
e.printStackTrace()
}
diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/runner/Utils.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/runner/Utils.kt
index fc0fdd1a6..88dfcf2e9 100644
--- a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/runner/Utils.kt
+++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/runner/Utils.kt
@@ -21,10 +21,11 @@ internal fun ResultWithDiagnostics.map(transformer: (T) -> R): ResultW
is ResultWithDiagnostics.Failure -> this
}
-internal fun EvaluationResult.valueOrNull() = returnValue.valueOrNull()
-internal fun ResultValue.valueOrNull(): Any? =
+internal fun EvaluationResult.value() = returnValue.value()
+internal fun ResultValue.value(): Any? =
when (this) {
is ResultValue.Value -> value
+ is ResultValue.Error -> throw error
else -> null
}