/* * 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; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.engine.framework.*; import com.volmit.iris.engine.hunk.Hunk; import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; 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.math.RNG; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Getter; import lombok.Setter; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull; import java.util.Random; public class IrisEngine extends BlockPopulator implements Engine { @Getter private final EngineCompound compound; @Getter private final EngineTarget target; @Getter private final EngineFramework framework; @Getter private final EngineEffects effects; @Getter private final EngineWorldManager worldManager; @Setter @Getter private volatile int parallelism; @Getter private final int index; @Getter private final EngineMetrics metrics; @Setter @Getter private volatile int minHeight; private boolean failing; private boolean closed; private int cacheId; private final int art; @Getter private double maxBiomeObjectDensity; @Getter private double maxBiomeLayerDensity; @Getter private double maxBiomeDecoratorDensity; public IrisEngine(EngineTarget target, EngineCompound compound, int index) { Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getHeight() + " height)"); metrics = new EngineMetrics(32); this.target = target; this.framework = new IrisEngineFramework(this); 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); J.a(this::computeBiomeMaxes); } 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 void close() { J.car(art); closed = true; getWorldManager().close(); getFramework().close(); getTarget().close(); } @Override public boolean isClosed() { return closed; } @Override public void recycle() { getFramework().recycle(); } @Override public double modifyX(double x) { return x / getDimension().getTerrainZoom(); } @Override public double modifyZ(double z) { return z / getDimension().getTerrainZoom(); } @ChunkCoordinates @Override public void generate(int x, int z, Hunk vblocks, Hunk vbiomes, boolean multicore) { try { PrecisionStopwatch p = PrecisionStopwatch.start(); Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y + getMinHeight(), z + zz, t)); switch (getDimension().getTerrainMode()) { case NORMAL -> { getFramework().getEngineParallax().generateParallaxArea(x >> 4, z >> 4); getFramework().getTerrainActuator().actuate(x, z, vblocks, multicore); getFramework().getBiomeActuator().actuate(x, z, vbiomes, multicore); getFramework().getCaveModifier().modify(x, z, vblocks, multicore); getFramework().getRavineModifier().modify(x, z, vblocks, multicore); getFramework().getPostModifier().modify(x, z, vblocks, multicore); getFramework().getDecorantActuator().actuate(x, z, blocks, multicore); getFramework().getEngineParallax().insertParallax(x >> 4, z >> 4, blocks); getFramework().getDepositModifier().modify(x, z, blocks, multicore); } case ISLANDS -> { getFramework().getTerrainActuator().actuate(x, z, vblocks, multicore); } } getMetrics().getTotal().put(p.getMilliseconds()); if (IrisSettings.get().getGeneral().isDebug()) { KList v = new KList<>(); KMap g = getMetrics().pull(); for (String i : g.sortKNumber()) { if (g.get(i) != null) { v.add(C.RESET + "" + C.LIGHT_PURPLE + i + ": " + C.UNDERLINE + C.BLUE + Form.duration(g.get(i), 0) + C.RESET + C.GRAY + ""); } } Iris.debug(v.toString(", ")); } } catch (Throwable e) { Iris.reportError(e); fail("Failed to generate " + x + ", " + z, e); } } @Override public IrisBiome getFocus() { if (getDimension().getFocus() == null || getDimension().getFocus().trim().isEmpty()) { return null; } return getData().getBiomeLoader().load(getDimension().getFocus()); } @Override public void hotloading() { close(); } @Override public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk c) { getWorldManager().spawnInitialEntities(c); updateChunk(c); placeTiles(c); } @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; } @Override public void hotload() { cacheId = RNG.r.nextInt(); } }