From 703e61dd5423252268551c013ec527489abd053d Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 4 Oct 2025 13:39:33 +0200 Subject: [PATCH] fix preprocessors not applying reliably --- .../com/volmit/iris/engine/IrisEngine.java | 1162 +++++++++-------- .../engine/platform/BukkitChunkGenerator.java | 2 + .../scripting/kotlin/base/EngineScript.kt | 2 +- .../kotlin/base/PreprocessorScript.kt | 11 +- .../environment/IrisExecutionEnvironment.kt | 12 +- .../IrisSimpleExecutionEnvironment.kt | 4 +- .../core/scripting/kotlin/runner/Utils.kt | 5 +- 7 files changed, 610 insertions(+), 588 deletions(-) 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 }