diff --git a/build.gradle b/build.gradle index 9e62e4237..7a9b56801 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,7 @@ plugins { } group 'com.volmit.iris' -version '1.7.2' +version '1.7.3' def apiVersion = '1.17' def name = getRootProject().getName() // See settings.gradle def main = 'com.volmit.iris.Iris' diff --git a/src/main/java/com/volmit/iris/Iris.java b/src/main/java/com/volmit/iris/Iris.java index 1cbfa74f9..f95233155 100644 --- a/src/main/java/com/volmit/iris/Iris.java +++ b/src/main/java/com/volmit/iris/Iris.java @@ -27,6 +27,7 @@ import com.volmit.iris.core.link.MultiverseCoreLink; import com.volmit.iris.core.link.MythicMobsLink; import com.volmit.iris.core.link.OraxenLink; import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.project.IrisProject; import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.object.biome.IrisBiome; @@ -93,6 +94,10 @@ public class Iris extends VolmitPlugin implements Listener { @com.volmit.iris.util.plugin.Command public CommandIrisStudio commandStudio; + public static VolmitSender getSender() { + return sender; + } + private void preEnable() { instance = this; services = new KMap<>(); @@ -427,7 +432,6 @@ public class Iris extends VolmitPlugin implements Listener { @Override public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { - IrisDimension dim; if (id == null || id.isEmpty()) { dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); @@ -448,6 +452,7 @@ public class Iris extends VolmitPlugin implements Listener { Iris.info("Resolved missing dimension, proceeding with generation."); } } + Iris.debug("Assuming IrisDimension: " + dim.getName()); IrisWorld w = IrisWorld.builder() @@ -458,7 +463,17 @@ public class Iris extends VolmitPlugin implements Listener { .minHeight(0) .maxHeight(256) .build(); - return new BukkitChunkGenerator(w, false, new File(w.worldFolder(), "iris"), dim.getName()); + + Iris.debug("Generator Config: " + w.toString()); + + File ff = new File(w.worldFolder(), "iris/pack"); + if(!ff.exists() || ff.listFiles().length == 0) + { + ff.mkdirs(); + service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile()); + } + + return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); } public static void msg(String string) { diff --git a/src/main/java/com/volmit/iris/core/project/loader/IrisData.java b/src/main/java/com/volmit/iris/core/project/loader/IrisData.java index 28c36af52..5b18e1fdc 100644 --- a/src/main/java/com/volmit/iris/core/project/loader/IrisData.java +++ b/src/main/java/com/volmit/iris/core/project/loader/IrisData.java @@ -82,6 +82,20 @@ public class IrisData { hotloaded(); } + public static void dereference() + { + dataLoaders.v().forEach(IrisData::cleanupEngine); + } + + public void cleanupEngine() + { + if(engine != null && engine.isClosed()) + { + engine = null; + Iris.debug("Dereferenced Data " + getId() + " " + getDataFolder()); + } + } + public static int cacheSize() { int m = 0; for(IrisData i : dataLoaders.values()) diff --git a/src/main/java/com/volmit/iris/core/service/LocateSVC.java b/src/main/java/com/volmit/iris/core/service/LocateSVC.java deleted file mode 100644 index 1d862a34c..000000000 --- a/src/main/java/com/volmit/iris/core/service/LocateSVC.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.volmit.iris.core.service; - -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.plugin.IrisService; -import com.volmit.iris.util.plugin.VolmitSender; -import org.bukkit.Bukkit; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; - -public class LocateSVC implements IrisService { - - @EventHandler - public void on(final PlayerCommandPreprocessEvent event) { - if (IrisToolbelt.isIrisWorld(event.getPlayer().getWorld())) { - VolmitSender sender = new VolmitSender(event.getPlayer()); - sender.sendMessage(C.YELLOW + "You cannot locate structures in Iris worlds through vanilla commands"); - sender.sendMessage("You can use:"); - // TODO: Convert this to have the correct command prefix - Bukkit.dispatchCommand(event.getPlayer(), "/ird studio find"); - } - } - - @Override - public void onEnable() { - - } - - @Override - public void onDisable() { - - } -} diff --git a/src/main/java/com/volmit/iris/core/service/ExecutionSVC.java b/src/main/java/com/volmit/iris/core/service/PreservationSVC.java similarity index 75% rename from src/main/java/com/volmit/iris/core/service/ExecutionSVC.java rename to src/main/java/com/volmit/iris/core/service/PreservationSVC.java index b9f369583..b6123d28d 100644 --- a/src/main/java/com/volmit/iris/core/service/ExecutionSVC.java +++ b/src/main/java/com/volmit/iris/core/service/PreservationSVC.java @@ -19,17 +19,21 @@ package com.volmit.iris.core.service; import com.volmit.iris.Iris; +import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.plugin.IrisService; +import com.volmit.iris.util.scheduling.Looper; import java.util.concurrent.ExecutorService; -public class ExecutionSVC implements IrisService +public class PreservationSVC implements IrisService { private KList threads = new KList<>(); private KList bursts = new KList<>(); private KList services = new KList<>(); + private Looper dereferencer; public void register(Thread t) { @@ -46,13 +50,31 @@ public class ExecutionSVC implements IrisService services.add(service); } + public void dereference() + { + IrisContext.dereference(); + IrisData.dereference(); + } + @Override public void onEnable() { - + /* + * Dereferences copies of Engine instances that are closed to prevent memory from + * hanging around and keeping copies of complex, caches and other dead objects. + */ + dereferencer = new Looper() { + @Override + protected long loop() { + dereference(); + return 60000; + } + }; } @Override public void onDisable() { + dereferencer.interrupt(); + for(Thread i : threads) { if(i.isAlive()) @@ -97,5 +119,7 @@ public class ExecutionSVC implements IrisService } } + + dereference(); } } diff --git a/src/main/java/com/volmit/iris/engine/IrisComplex.java b/src/main/java/com/volmit/iris/engine/IrisComplex.java index 6888284d3..4d7fb4e37 100644 --- a/src/main/java/com/volmit/iris/engine/IrisComplex.java +++ b/src/main/java/com/volmit/iris/engine/IrisComplex.java @@ -47,6 +47,7 @@ import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import java.lang.reflect.Field; import java.util.List; import java.util.UUID; @@ -76,11 +77,13 @@ public class IrisComplex implements DataProvider { private ProceduralStream trueBiomeDerivativeStream; private ProceduralStream heightStream; private ProceduralStream heightStreamNoFeatures; + private ProceduralStream heightDomainStreamNoFeatures; private ProceduralStream objectChanceStream; private ProceduralStream maxHeightStream; private ProceduralStream overlayStream; private ProceduralStream heightFluidStream; private ProceduralStream trueHeightStream; + private ProceduralStream trueHeightDomainStream; private ProceduralStream slopeStream; private ProceduralStream islandTopStream; private ProceduralStream islandBottomStream; @@ -226,6 +229,25 @@ public class IrisComplex implements DataProvider { IrisBiome b = focus != null ? focus : baseBiomeStream.get(x, z); return getHeight(engine, b, x, z, engine.getWorld().seed(), false); }, Interpolated.DOUBLE).clamp(0, engine.getHeight()).cache2D(cacheSize); + heightDomainStreamNoFeatures = engine.getDimension().getVerticalDomain().isFlat() + ? heightStreamNoFeatures + : ProceduralStream.of((x, z) -> { + double hh = 0; + double v, i; + + for(i = 0; i < engine.getHeight(); i++) + { + double ox = engine.getDimension().getVerticalDomain().get(rng, getData(), i - 12345); + double oz = engine.getDimension().getVerticalDomain().get(rng, getData(), i + 54321); + v = heightStreamNoFeatures.get(x+ox, z+oz); + if(v > hh) + { + hh = v; + } + } + + return hh; + }, Interpolated.DOUBLE).cache2D(cacheSize); slopeStream = heightStream.slope(3).cache2D(cacheSize); objectChanceStream = ProceduralStream.ofDouble((x, z) -> { if (engine.getDimension().hasFeatures(engine)) { @@ -323,6 +345,7 @@ public class IrisComplex implements DataProvider { .convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_SURFACE)).cache2D(cacheSize); seaFloorDecoration = trueBiomeStream .convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_FLOOR)).cache2D(cacheSize); + trueHeightStream = ProceduralStream.of((x, z) -> { int rx = (int) Math.round(engine.modifyX(x)); int rz = (int) Math.round(engine.modifyZ(z)); @@ -370,6 +393,26 @@ public class IrisComplex implements DataProvider { .get(x, z) : 0); islandBottomStream = islandStream.convertAware2D((i, x, z) -> i ? islandHeightStream.subtract(islandDepthStream).round().get(x, z) : 0); + + trueHeightDomainStream = engine.getDimension().getVerticalDomain().isFlat() + ? trueHeightStream + : ProceduralStream.of((x, z) -> { + double hh = 0; + double v, i; + + for(i = 0; i < engine.getHeight(); i++) + { + double ox = engine.getDimension().getVerticalDomain().get(rng, getData(), i - 12345); + double oz = engine.getDimension().getVerticalDomain().get(rng, getData(), i + 54321); + v = trueHeightStream.get(x+ox, z+oz); + if(v > hh) + { + hh = v; + } + } + + return (int)Math.round(hh); + }, Interpolated.INT).cache2D(cacheSize); //@done } @@ -502,4 +545,8 @@ public class IrisComplex implements DataProvider { biome.setInferredType(b.getInferredType()); return implode(biome, x, z, max - 1); } + + public void close() { + + } } diff --git a/src/main/java/com/volmit/iris/engine/IrisEngine.java b/src/main/java/com/volmit/iris/engine/IrisEngine.java index 5cc9c5067..5bea6a8dc 100644 --- a/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -22,6 +22,8 @@ import com.google.common.util.concurrent.AtomicDouble; import com.google.gson.Gson; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.events.IrisEngineHotloadEvent; +import com.volmit.iris.core.service.PreservationSVC; import com.volmit.iris.engine.actuator.IrisBiomeActuator; import com.volmit.iris.engine.actuator.IrisDecorantActuator; import com.volmit.iris.engine.actuator.IrisTerrainIslandActuator; @@ -65,8 +67,6 @@ import org.bukkit.generator.BlockPopulator; import java.io.File; import java.io.IOException; import java.util.Random; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -81,11 +81,11 @@ public class IrisEngine extends BlockPopulator implements Engine { private final AtomicLong lastGPS; private final EngineTarget target; private final IrisContext context; - private final EngineEffects effects; + private EngineEffects effects; private final EngineMantle mantle; private final ChronoLatch perSecondLatch; - private final EngineExecutionEnvironment execution; - private final EngineWorldManager worldManager; + private EngineExecutionEnvironment execution; + private EngineWorldManager worldManager; private volatile int parallelism; private final EngineMetrics metrics; private volatile int minHeight; @@ -98,57 +98,93 @@ public class IrisEngine extends BlockPopulator implements Engine { private double maxBiomeObjectDensity; private double maxBiomeLayerDensity; private double maxBiomeDecoratorDensity; - private final IrisComplex complex; - private final EngineActuator terrainNormalActuator; - private final EngineActuator terrainIslandActuator; - private final EngineActuator decorantActuator; - private final EngineActuator biomeActuator; - private final EngineModifier depositModifier; - private final EngineModifier caveModifier; - private final EngineModifier ravineModifier; - private final EngineModifier postModifier; + private IrisComplex complex; + private EngineActuator terrainNormalActuator; + private EngineActuator terrainIslandActuator; + private EngineActuator decorantActuator; + private EngineActuator biomeActuator; + private EngineModifier depositModifier; + private EngineModifier caveModifier; + private EngineModifier ravineModifier; + private EngineModifier postModifier; private final AtomicCache engineData = new AtomicCache<>(); private final AtomicBoolean cleaning; private final ChronoLatch cleanLatch; public IrisEngine(EngineTarget target, boolean studio) { this.studio = studio; + this.target = target; + metrics = new EngineMetrics(32); + cleanLatch = new ChronoLatch(Math.max(10000, Math.min(IrisSettings.get().getParallax() + .getParallaxChunkEvictionMS(), IrisSettings.get().getParallax().getParallaxRegionEvictionMS()))); generatedLast = new AtomicInteger(0); perSecond = new AtomicDouble(0); perSecondLatch = new ChronoLatch(1000, false); wallClock = new AtomicRollingSequence(32); lastGPS = new AtomicLong(M.ms()); generated = new AtomicInteger(0); - execution = new IrisExecutionEnvironment(this); - // TODO: HEIGHT ------------------------------------------------------------------------------------------------------> + mantle = new IrisEngineMantle(this); + context = new IrisContext(this); + cleaning = new AtomicBoolean(false); + context.touch(); 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); minHeight = 0; failing = false; closed = false; + art = J.ar(this::tickRandomPlayer, 0); + setupEngine(); + } + + private void tickRandomPlayer() { + if(effects != null) { + effects.tickRandomPlayer(); + } + } + + private void prehotload() + { + worldManager.close(); + complex.close(); + execution.close(); + terrainNormalActuator.close(); + terrainIslandActuator.close(); + decorantActuator.close(); + biomeActuator.close(); + depositModifier.close(); + ravineModifier.close(); + caveModifier.close(); + postModifier.close(); + effects.close(); + } + + private void setupEngine() + { cacheId = RNG.r.nextInt(); + worldManager = new IrisWorldManager(this); + complex = new IrisComplex(this); + execution = new IrisExecutionEnvironment(this); + terrainNormalActuator = new IrisTerrainNormalActuator(this); + terrainIslandActuator = new IrisTerrainIslandActuator(this); + decorantActuator = new IrisDecorantActuator(this); + biomeActuator = new IrisBiomeActuator(this); + depositModifier = new IrisDepositModifier(this); + ravineModifier = new IrisRavineModifier(this); + caveModifier = new IrisCaveModifier(this); + postModifier = new IrisPostModifier(this); effects = new IrisEngineEffects(this); - art = J.ar(effects::tickRandomPlayer, 0); J.a(this::computeBiomeMaxes); - context = new IrisContext(this); - context.touch(); - this.complex = new IrisComplex(this); - this.terrainNormalActuator = new IrisTerrainNormalActuator(this); - this.terrainIslandActuator = new IrisTerrainIslandActuator(this); - this.decorantActuator = new IrisDecorantActuator(this); - this.biomeActuator = new IrisBiomeActuator(this); - this.depositModifier = new IrisDepositModifier(this); - this.ravineModifier = new IrisRavineModifier(this); - this.caveModifier = new IrisCaveModifier(this); - this.postModifier = new IrisPostModifier(this); - cleaning = new AtomicBoolean(false); - cleanLatch = new ChronoLatch(Math.max(10000, Math.min(IrisSettings.get().getParallax() - .getParallaxChunkEvictionMS(), IrisSettings.get().getParallax().getParallaxRegionEvictionMS()))); - mantle = new IrisEngineMantle(this); + } + + @Override + public void hotload() { + getData().dump(); + getData().clearLists(); + getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey())); + prehotload(); + setupEngine(); + Iris.callEvent(new IrisEngineHotloadEvent(this)); } @Override @@ -302,6 +338,10 @@ public class IrisEngine extends BlockPopulator implements Engine { getCaveModifier().close(); getPostModifier().close(); getMantle().close(); + getComplex().close(); + getData().dump(); + getData().clearLists(); + Iris.service(PreservationSVC.class).dereference(); Iris.debug("Engine Fully Shutdown!"); } diff --git a/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index 070366de0..81e6f7dab 100644 --- a/src/main/java/com/volmit/iris/engine/IrisWorldManager.java +++ b/src/main/java/com/volmit/iris/engine/IrisWorldManager.java @@ -57,6 +57,7 @@ import java.util.stream.Stream; @Data public class IrisWorldManager extends EngineAssignedWorldManager { private final Looper looper; + private final int id; private final KMap chunkCooldowns; private double energy = 25; private int entityCount = 0; @@ -75,6 +76,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { cln = null; chunkCooldowns = null; looper = null; + id = -1; } public IrisWorldManager(Engine engine) { @@ -83,11 +85,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager { cl = new ChronoLatch(3000); ecl = new ChronoLatch(250); chunkCooldowns = new KMap<>(); + id = engine.getCacheID(); energy = 25; looper = new Looper() { @Override protected long loop() { - if (getEngine().isClosed()) { + if (getEngine().isClosed() || getEngine().getCacheID() != id) { interrupt(); } @@ -186,9 +189,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { } Chunk c = cc[RNG.r.nextInt(cc.length)]; - IrisBiome biome = getEngine().getSurfaceBiome(c); - IrisRegion region = getEngine().getRegion(c); - spawnIn(c, biome, region); + spawnIn(c, false); chunkCooldowns.put(Cache.key(c), M.ms()); } @@ -200,26 +201,29 @@ public class IrisWorldManager extends EngineAssignedWorldManager { energy = M.clip(energy, 1D, 1000D); } - private void spawnIn(Chunk c, IrisBiome biome, IrisRegion region) { + private void spawnIn(Chunk c, boolean initial) { + IrisBiome biome = getEngine().getSurfaceBiome(c); + IrisRegion region = getEngine().getRegion(c); //@builder IrisEntitySpawn v = spawnRandomly(Stream.concat(Stream.concat( getData().getSpawnerLoader() .loadAll(getDimension().getEntitySpawners()) - .shuffleCopy(RNG.r).stream().filter(this::canSpawn), + .shuffleCopy(RNG.r).stream() + .filter(this::canSpawn), getData().getSpawnerLoader().streamAll(getEngine().getMantle() .getFeaturesInChunk(c).stream() .flatMap((o) -> o.getFeature().getEntitySpawners().stream())) .filter(this::canSpawn)) .filter((i) -> i.isValid(biome)) - .flatMap(this::stream), + .flatMap((i) -> stream(i, initial)), Stream.concat(getData().getSpawnerLoader() .loadAll(getEngine().getRegion(c.getX() << 4, c.getZ() << 4).getEntitySpawners()) .shuffleCopy(RNG.r).stream().filter(this::canSpawn) - .flatMap(this::stream), + .flatMap((i) -> stream(i, initial)), getData().getSpawnerLoader() .loadAll(getEngine().getSurfaceBiome(c.getX() << 4, c.getZ() << 4).getEntitySpawners()) .shuffleCopy(RNG.r).stream().filter(this::canSpawn) - .flatMap(this::stream))) + .flatMap((i) -> stream(i, initial)))) .collect(Collectors.toList())) .popRandom(RNG.r); @@ -279,12 +283,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager { } } - private Stream stream(IrisSpawner s) { - for (IrisEntitySpawn i : s.getSpawns()) { + private Stream stream(IrisSpawner s, boolean initial) { + for (IrisEntitySpawn i : initial ? s.getInitialSpawns() : s.getSpawns()) { i.setReferenceSpawner(s); } - return s.getSpawns().stream(); + return (initial ? s.getInitialSpawns() : s.getSpawns()).stream(); } private KList spawnRandomly(List types) { @@ -340,6 +344,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { public void onChunkLoad(Chunk e, boolean generated) { if (generated) { energy += 1.2; + spawnIn(e, true); } else { energy += 0.3; } diff --git a/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainIslandActuator.java b/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainIslandActuator.java index d67da5335..e9890d3ea 100644 --- a/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainIslandActuator.java +++ b/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainIslandActuator.java @@ -40,7 +40,6 @@ public class IrisTerrainIslandActuator extends EngineAssignedActuator @Getter private final RNG rng; private final boolean carving; - private final boolean hasUnder; @Getter private final int lastBedrock = -1; @@ -48,7 +47,6 @@ public class IrisTerrainIslandActuator extends EngineAssignedActuator super(engine, "TerrainIsland"); rng = new RNG(engine.getWorld().seed()); carving = getDimension().isCarving() && getDimension().getCarveLayers().isNotEmpty(); - hasUnder = getDimension().getUndercarriage() != null && !getDimension().getUndercarriage().getGenerator().isFlat(); } @BlockCoordinates diff --git a/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java b/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java index 3d4b13800..36b50ee35 100644 --- a/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java +++ b/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java @@ -18,9 +18,11 @@ package com.volmit.iris.engine.actuator; +import com.volmit.iris.Iris; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineAssignedActuator; import com.volmit.iris.engine.object.biome.IrisBiome; +import com.volmit.iris.engine.object.noise.IrisShapedGeneratorStyle; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.hunk.Hunk; @@ -38,15 +40,16 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator @Getter private final RNG rng; private final boolean carving; - private final boolean hasUnder; @Getter private int lastBedrock = -1; + private IrisShapedGeneratorStyle domain; public IrisTerrainNormalActuator(Engine engine) { super(engine, "Terrain"); rng = new RNG(engine.getWorld().seed()); carving = getDimension().isCarving() && getDimension().getCarveLayers().isNotEmpty(); - hasUnder = getDimension().getUndercarriage() != null && !getDimension().getUndercarriage().getGenerator().isFlat(); + domain = getDimension().getVerticalDomain(); + domain = domain.isFlat() ? null : domain; } @BlockCoordinates @@ -71,6 +74,75 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator getEngine().getMetrics().getTerrain().put(p.getMilliseconds()); } + public void generateGround(int realX, int realZ, int xf, int zf, Hunk h, int surface, int bottom, int height, int fluidOrHeight, IrisBiome biome) + { + if(surface == bottom || surface-1 == bottom) + { + return; + } + + KList blocks = null; + KList fblocks = null; + int depth,fdepth; + + for (int i = surface; i >= bottom; i--) { + if (i >= h.getHeight()) { + continue; + } + + if (i == 0) { + if (getDimension().isBedrock()) { + h.set(xf, i, zf, BEDROCK); + lastBedrock = i; + continue; + } + } + + if (carving && getDimension().isCarved(getData(), realX, i, realZ, rng, height)) { + continue; + } + + if (getDimension().getCaverns() != null && getDimension().getCaverns().isCavern(rng, realX, i, realZ, height, getData())) { + continue; + } + + if (i > height && i <= fluidOrHeight) { + fdepth = fluidOrHeight - i; + + if (fblocks == null) { + fblocks = biome.generateSeaLayers(realX, realZ, rng, fluidOrHeight - height, getData()); + } + + if (fblocks.hasIndex(fdepth)) { + h.set(xf, i, zf, fblocks.get(fdepth)); + continue; + } + + h.set(xf, i, zf, getComplex().getFluidStream().get(realX, +realZ)); + continue; + } + + if (i <= height) { + depth = surface - i; + if (blocks == null) { + blocks = biome.generateLayers(realX, realZ, rng, surface - bottom, surface, getData(), getComplex()); + } + + if (blocks.hasIndex(depth)) { + h.set(xf, i, zf, blocks.get(depth)); + continue; + } + + h.set(xf, i, zf, getComplex().getRockStream().get(realX, realZ)); + } + } + } + + private int fluidOrHeight(int height) + { + return Math.max(getDimension().getFluidHeight(), height); + } + /** * This is calling 1/16th of a chunk x/z slice. It is a plane from sky to bedrock 1 thick in the x direction. * @@ -81,74 +153,63 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator */ @BlockCoordinates public void terrainSliver(int x, int z, int xf, Hunk h) { - int i, depth, realX, realZ, hf, he, b, fdepth; + int i, j, k, realX, realZ, hf, he; IrisBiome biome; - KList blocks, fblocks; - for (int zf = 0; zf < h.getDepth(); zf++) { + for (i = 0; i < h.getDepth(); i++) { realX = (int) modX(xf + x); - realZ = (int) modZ(zf + z); - b = hasUnder ? (int) Math.round(getDimension().getUndercarriage().get(rng, getData(), realX, realZ)) : 0; - he = (int) Math.round(Math.min(h.getHeight(), getComplex().getHeightStream().get(realX, realZ))); - hf = Math.round(Math.max(Math.min(h.getHeight(), getDimension().getFluidHeight()), he)); - biome = getComplex().getTrueBiomeStream().get(realX, realZ); - blocks = null; - fblocks = null; + realZ = (int) modZ(i + z); - if (hf < b) { - continue; + if(domain != null) + { + int[] heights = new int[h.getHeight()]; + IrisBiome[] biomes = new IrisBiome[h.getHeight()]; + int maximum = 0; + + for(j = 0; j < h.getHeight(); j++) + { + double ox = domain.get(rng, getData(), j - 12345); + double oz = domain.get(rng, getData(), j + 54321); + biomes[j] = getComplex().getTrueBiomeStream().get(realX+ox, realZ+oz); + heights[j] = (int) Math.round(Math.min(h.getHeight(), getComplex().getHeightStream().get(realX+ox, realZ+oz))); + maximum = Math.max(maximum, heights[j]); + } + + for(j = maximum; j >= 0; j--) { + if(fluidOrHeight(heights[j]) < j) + { + continue; + } + + int hi = j; + int lo = 0; + + for(k = j; k >= 0; k--) + { + if(fluidOrHeight(heights[k]) < k) + { + break; + } + + lo = k; + j = k-1; + } + + generateGround(realX, realZ, xf, i, h, hi, lo, heights[hi], fluidOrHeight(heights[hi]), biomes[hi]); + } } - for (i = hf; i >= b; i--) { - if (i >= h.getHeight()) { + else + { + biome = getComplex().getTrueBiomeStream().get(realX, realZ); + he = (int) Math.round(Math.min(h.getHeight(), getComplex().getHeightStream().get(realX, realZ))); + hf = Math.round(Math.max(Math.min(h.getHeight(), getDimension().getFluidHeight()), he)); + + if (hf < 0) { continue; } - if (i == b) { - if (getDimension().isBedrock()) { - h.set(xf, i, zf, BEDROCK); - lastBedrock = i; - continue; - } - } - - if (carving && getDimension().isCarved(getData(), realX, i, realZ, rng, he)) { - continue; - } - - if (getDimension().getCaverns() != null && getDimension().getCaverns().isCavern(rng, realX, i, realZ, he, getData())) { - continue; - } - - if (i > he && i <= hf) { - fdepth = hf - i; - - if (fblocks == null) { - fblocks = biome.generateSeaLayers(realX, realZ, rng, hf - he, getData()); - } - - if (fblocks.hasIndex(fdepth)) { - h.set(xf, i, zf, fblocks.get(fdepth)); - continue; - } - - h.set(xf, i, zf, getComplex().getFluidStream().get(realX, +realZ)); - continue; - } - - if (i <= he) { - depth = he - i; - if (blocks == null) { - blocks = biome.generateLayers(realX, realZ, rng, he, he, getData(), getComplex()); - } - - if (blocks.hasIndex(depth)) { - h.set(xf, i, zf, blocks.get(depth)); - continue; - } - - h.set(xf, i, zf, getComplex().getRockStream().get(realX, realZ)); - } + generateGround(realX, realZ, xf, i, h, hf, 0, he, hf, biome); } } } 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 d16c4724a..0a3e8e02b 100644 --- a/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -77,7 +77,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdater, Renderer { +public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdater, Renderer, Hotloadable { IrisComplex getComplex(); void printMetrics(CommandSender sender); 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 4bdaa0ebe..250b343cb 100644 --- a/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java +++ b/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java @@ -28,7 +28,7 @@ import lombok.Data; @Data public class EngineTarget { private final MultiBurst burster; - private final IrisDimension dimension; + private IrisDimension dimension; private IrisWorld world; private final IrisData data; 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 6b681a72a..9983bbf0d 100644 --- a/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -76,6 +76,8 @@ public class PlannedStructure { generateTerminators(); + Iris.debug("JPlace: ROOT @ relative " + position.toString()); + for (PlannedPiece i : pieces) { Iris.debug("Place: " + i.getObject().getLoadKey() + " at @ relative " + i.getPosition().toString()); } @@ -112,7 +114,9 @@ public class PlannedStructure { int xx = i.getPosition().getX() + sx; int zz = i.getPosition().getZ() + sz; int offset = i.getPosition().getY() - startHeight; - int height = placer.getHighest(xx, zz, getData()) + offset + (v.getH() / 2); + int height = (i.getStructure().getStructure().getLockY() != -1 + ? i.getStructure().getStructure().getLockY() + : placer.getHighest(xx, zz, getData())) + offset + (v.getH() / 2); if (options.getMode().equals(ObjectPlaceMode.PAINT) || options.isVacuum()) { height = -1; @@ -122,7 +126,6 @@ public class PlannedStructure { int h = vo.place(xx, height, zz, placer, options, rng, (b) -> e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id), null, getData()); - for (IrisJigsawPieceConnector j : i.getAvailableConnectors()) { if (j.getSpawnEntity() != null)// && h != -1) { @@ -155,27 +158,6 @@ public class PlannedStructure { public void place(World world) { for (PlannedPiece i : pieces) { - Iris.sq(() -> { - for (IrisJigsawPieceConnector j : i.getAvailableConnectors()) { - if (j.getSpawnEntity() != null) { - Engine a = IrisToolbelt.access(world).getEngine(); - if (a == null) { - Iris.warn("Cannot spawn entities from jigsaw in non Iris world!"); - break; - } - IrisPosition p = i.getWorldPosition(j).add(new IrisPosition(j.getDirection().toVector().multiply(2))); - IrisEntity e = getData().getEntityLoader().load(j.getSpawnEntity()); - - if (a != null) { - 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); - } - } - } - } - }); - Iris.sq(() -> i.place(world)); } } @@ -246,7 +228,9 @@ public class PlannedStructure { private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea, IrisObjectRotation rotation) { if (!idea.getPlacementOptions().getRotation().isEnabled()) - rotation = piece.getRotation(); //Inherit parent rotation + { + rotation = piece.getRotation(); + } PlannedPiece test = new PlannedPiece(this, piece.getPosition(), idea, rotation); diff --git a/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index 9eae3af89..37d34a44f 100644 --- a/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -44,6 +44,7 @@ import org.bukkit.block.data.BlockData; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; @@ -189,8 +190,13 @@ public interface EngineMantle extends IObjectPlacer { } PrecisionStopwatch p = PrecisionStopwatch.start(); - List post = Collections.synchronizedList(new KList<>()); - Consumer c = post::add; + KList post = new KList<>(); + Consumer c = (i) -> { + synchronized (post) + { + post.add(i); + } + }; int s = getRealRadius(); BurstExecutor burst = burst().burst(); @@ -205,7 +211,13 @@ public interface EngineMantle extends IObjectPlacer { } burst.complete(); - burst().burst(post); + + while(!post.isEmpty()) + { + KList px = post.copy(); + post.clear(); + burst().burst(px); + } } default void generateMantleComponent(int x, int z, MantleComponent c, Consumer post) { 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 05cb8144d..4994393f9 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 @@ -35,6 +35,7 @@ import com.volmit.iris.engine.object.jigsaw.IrisJigsawStructurePlacement; import com.volmit.iris.engine.object.loot.IrisLootReference; import com.volmit.iris.engine.object.meta.IrisEffect; import com.volmit.iris.engine.object.noise.IrisGeneratorStyle; +import com.volmit.iris.engine.object.noise.IrisShapedGeneratorStyle; import com.volmit.iris.engine.object.noise.IrisSlopeClip; import com.volmit.iris.engine.object.noise.NoiseStyle; import com.volmit.iris.engine.object.objects.IrisObject; diff --git a/src/main/java/com/volmit/iris/engine/object/deposits/IrisDepositGenerator.java b/src/main/java/com/volmit/iris/engine/object/deposits/IrisDepositGenerator.java index 2fb8e4ba8..9cb1607fe 100644 --- a/src/main/java/com/volmit/iris/engine/object/deposits/IrisDepositGenerator.java +++ b/src/main/java/com/volmit/iris/engine/object/deposits/IrisDepositGenerator.java @@ -22,6 +22,7 @@ import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.block.IrisBlockData; +import com.volmit.iris.engine.object.noise.IrisGenerator; import com.volmit.iris.engine.object.objects.IrisObject; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.math.RNG; diff --git a/src/main/java/com/volmit/iris/engine/object/dimensional/IrisDimension.java b/src/main/java/com/volmit/iris/engine/object/dimensional/IrisDimension.java index ca571c184..18799e2e5 100644 --- a/src/main/java/com/volmit/iris/engine/object/dimensional/IrisDimension.java +++ b/src/main/java/com/volmit/iris/engine/object/dimensional/IrisDimension.java @@ -39,6 +39,7 @@ import com.volmit.iris.engine.object.jigsaw.IrisJigsawStructure; import com.volmit.iris.engine.object.jigsaw.IrisJigsawStructurePlacement; import com.volmit.iris.engine.object.loot.IrisLootReference; import com.volmit.iris.engine.object.noise.IrisGeneratorStyle; +import com.volmit.iris.engine.object.noise.IrisInterpolator; import com.volmit.iris.engine.object.noise.IrisShapedGeneratorStyle; import com.volmit.iris.engine.object.noise.NoiseStyle; import com.volmit.iris.engine.object.objects.IrisObjectPlacement; @@ -106,9 +107,6 @@ public class IrisDimension extends IrisRegistrant { @Desc("Define iris cavern zones") private IrisCaverns caverns = new IrisCaverns(); - @Desc("Instead of a flat bottom, applies a clamp (using this noise style) to the bottom instead of a flat bottom. Useful for carving out center-dimensions in a dimension composite world.") - private IrisShapedGeneratorStyle undercarriage = null; - @Desc("Upon joining this world, Iris will send a resource pack request to the client. If they have previously selected yes, it will auto-switch depending on which dimension they go to.") private String resourcePack = ""; @@ -332,6 +330,9 @@ public class IrisDimension extends IrisRegistrant { @Desc("Cartographer map trade overrides") private IrisVillagerOverride patchCartographers = new IrisVillagerOverride().setDisableTrade(false); + @Desc("If defined, warp the terrain vertically by this style. This will cause overhangs & carving-like shapes") + private IrisShapedGeneratorStyle verticalDomain = new IrisShapedGeneratorStyle(NoiseStyle.FLAT, 1, 1); + private final transient AtomicCache parallaxSize = new AtomicCache<>(); private final transient AtomicCache rockLayerGenerator = new AtomicCache<>(); private final transient AtomicCache fluidLayerGenerator = new AtomicCache<>(); diff --git a/src/main/java/com/volmit/iris/engine/object/jigsaw/IrisJigsawStructure.java b/src/main/java/com/volmit/iris/engine/object/jigsaw/IrisJigsawStructure.java index 143427054..af36ce9fa 100644 --- a/src/main/java/com/volmit/iris/engine/object/jigsaw/IrisJigsawStructure.java +++ b/src/main/java/com/volmit/iris/engine/object/jigsaw/IrisJigsawStructure.java @@ -59,6 +59,9 @@ public class IrisJigsawStructure extends IrisRegistrant { @Desc("If set to true, iris will look for any pieces with only one connector in valid pools for edge connectors and attach them to 'terminate' the paths/piece connectors. Essentially it caps off ends. For example in a village, Iris would add houses to the ends of roads where possible. For terminators to be selected, they can only have one connector or they wont be chosen.") private boolean terminate = true; + @Desc("Set to lock the starting peice to a y coordinate, otherwise the surface will be used.") + private int lockY = -1; + private transient AtomicCache maxDimension = new AtomicCache<>(); private void loadPool(String p, KList pools, KList pieces) { diff --git a/src/main/java/com/volmit/iris/engine/object/noise/IrisGenerator.java b/src/main/java/com/volmit/iris/engine/object/noise/IrisGenerator.java index bbc1393d0..6a1c0d12c 100644 --- a/src/main/java/com/volmit/iris/engine/object/noise/IrisGenerator.java +++ b/src/main/java/com/volmit/iris/engine/object/noise/IrisGenerator.java @@ -24,6 +24,7 @@ import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.common.IRare; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.interpolation.IrisInterpolation; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.noise.CellGenerator; diff --git a/src/main/java/com/volmit/iris/engine/object/noise/IrisInterpolator.java b/src/main/java/com/volmit/iris/engine/object/noise/IrisInterpolator.java index c38fbcade..73bbf1471 100644 --- a/src/main/java/com/volmit/iris/engine/object/noise/IrisInterpolator.java +++ b/src/main/java/com/volmit/iris/engine/object/noise/IrisInterpolator.java @@ -38,13 +38,13 @@ import lombok.experimental.Accessors; public class IrisInterpolator { @Required @Desc("The interpolation method when two biomes use different heights but this same generator") - private InterpolationMethod function = InterpolationMethod.BICUBIC; + private InterpolationMethod function = InterpolationMethod.BILINEAR_STARCAST_6; @Required @MinNumber(1) @MaxNumber(8192) @Desc("The range checked horizontally. Smaller ranges yeild more detail but are not as smooth.") - private double horizontalScale = 3; + private double horizontalScale = 7; public double interpolate(double x, double z, NoiseProvider provider) { return interpolate((int) Math.round(x), (int) Math.round(z), provider); diff --git a/src/main/java/com/volmit/iris/engine/object/noise/IrisShapedGeneratorStyle.java b/src/main/java/com/volmit/iris/engine/object/noise/IrisShapedGeneratorStyle.java index 3404ddd46..1c5273dbc 100644 --- a/src/main/java/com/volmit/iris/engine/object/noise/IrisShapedGeneratorStyle.java +++ b/src/main/java/com/volmit/iris/engine/object/noise/IrisShapedGeneratorStyle.java @@ -64,4 +64,8 @@ public class IrisShapedGeneratorStyle { public IrisShapedGeneratorStyle(NoiseStyle style) { this.generator = new IrisGeneratorStyle(style); } + + public boolean isFlat() { + return min == max || getGenerator().isFlat(); + } } diff --git a/src/main/java/com/volmit/iris/engine/object/objects/IrisAxisRotationClamp.java b/src/main/java/com/volmit/iris/engine/object/objects/IrisAxisRotationClamp.java index bcb0b53fc..80ed6d02c 100644 --- a/src/main/java/com/volmit/iris/engine/object/objects/IrisAxisRotationClamp.java +++ b/src/main/java/com/volmit/iris/engine/object/objects/IrisAxisRotationClamp.java @@ -33,6 +33,7 @@ import lombok.experimental.Accessors; public class IrisAxisRotationClamp { @Desc("Should this axis be rotated at all?") private boolean enabled = false; + private transient boolean forceLock = false; @Required @@ -72,7 +73,7 @@ public class IrisAxisRotationClamp { public double getRadians(int rng) { if (forceLock) { - return Math.toRadians(max); + return Math.toRadians(Math.ceil(Math.abs((max % 360D)))); } if (isUnlimited()) { diff --git a/src/main/java/com/volmit/iris/engine/object/regional/IrisRegion.java b/src/main/java/com/volmit/iris/engine/object/regional/IrisRegion.java index 3818dda10..c24ea2ab8 100644 --- a/src/main/java/com/volmit/iris/engine/object/regional/IrisRegion.java +++ b/src/main/java/com/volmit/iris/engine/object/regional/IrisRegion.java @@ -34,6 +34,7 @@ import com.volmit.iris.engine.object.jigsaw.IrisJigsawStructurePlacement; import com.volmit.iris.engine.object.loot.IrisLootReference; import com.volmit.iris.engine.object.meta.IrisEffect; import com.volmit.iris.engine.object.noise.IrisGeneratorStyle; +import com.volmit.iris.engine.object.noise.IrisShapedGeneratorStyle; import com.volmit.iris.engine.object.noise.NoiseStyle; import com.volmit.iris.engine.object.objects.IrisObjectPlacement; import com.volmit.iris.engine.object.spawners.IrisSpawner; diff --git a/src/main/java/com/volmit/iris/engine/object/spawners/IrisSpawner.java b/src/main/java/com/volmit/iris/engine/object/spawners/IrisSpawner.java index 116f2afdb..a7773979e 100644 --- a/src/main/java/com/volmit/iris/engine/object/spawners/IrisSpawner.java +++ b/src/main/java/com/volmit/iris/engine/object/spawners/IrisSpawner.java @@ -45,6 +45,10 @@ public class IrisSpawner extends IrisRegistrant { @Desc("The entity spawns to add") private KList spawns = new KList<>(); + @ArrayType(min = 1, type = IrisEntitySpawn.class) + @Desc("The entity spawns to add initially. EXECUTES PER CHUNK!") + private KList initialSpawns = new KList<>(); + @Desc("The energy multiplier when calculating spawn energy usage") private double energyMultiplier = 1; diff --git a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 0500e0e87..a5859e21e 100644 --- a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -19,10 +19,15 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; +import com.volmit.iris.core.project.loader.IrisData; +import com.volmit.iris.core.service.StudioSVC; +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.framework.WrongEngineBroException; 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; @@ -30,6 +35,8 @@ import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; @@ -39,14 +46,18 @@ import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; import org.jetbrains.annotations.NotNull; +import javax.management.RuntimeErrorException; import java.io.File; import java.util.List; import java.util.Random; import java.util.concurrent.Semaphore; +@EqualsAndHashCode(callSuper = true) +@Data public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator { - private static final int HOTLOAD_LOCKS = 1000000; - private final EngineProvider provider; + private static final int LOAD_LOCKS = 1_000_000; + private final Semaphore loadLock; + private final Engine engine; private final IrisWorld world; private final File dataLocation; private final String dimensionKey; @@ -54,21 +65,56 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun private final KList populators; private final ChronoLatch hotloadChecker; private final Looper hotloader; - private final Semaphore hotloadLock; private final boolean studio; public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) { populators = new KList<>(); + loadLock = new Semaphore(LOAD_LOCKS); this.world = world; - this.hotloadLock = new Semaphore(HOTLOAD_LOCKS); this.hotloadChecker = new ChronoLatch(1000, false); this.studio = studio; this.dataLocation = dataLocation; this.dimensionKey = dimensionKey; this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> hotload()); - this.provider = new EngineProvider(); - initialize(); + IrisData data = IrisData.get(dataLocation); + IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); + if(dimension == null) + { + Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); + IrisDimension test = IrisData.loadAnyDimension(dimensionKey); + + if(test != null) + { + Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath()); + Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); + Iris.warn("Attempted to install into " + data.getDataFolder().getPath()); + data.dump(); + data.clearLists(); + test = data.getDimensionLoader().load(dimensionKey); + + if(test != null) + { + Iris.success("Woo! Patched the Engine to work with MVC bugs. Have a nice day!"); + dimension = test; + } + + else + { + Iris.error("Failed to patch dimension!"); + throw new RuntimeException("Missing Dimension: " + dimensionKey); + } + } + + else + { + Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?"); + throw new RuntimeException("Missing Dimension: " + dimensionKey); + } + } + + this.engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); + populators.add((BlockPopulator) engine); this.hotloader = new Looper() { @Override protected long loop() { @@ -84,13 +130,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun hotloader.setName(getTarget().getWorld().name() + " Hotloader"); } - public synchronized Engine getEngine() { - synchronized (provider) - { - return provider.getEngine(); - } - } - @Override public boolean isHeadless() { return false; @@ -98,11 +137,10 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @Override public void close() { - synchronized (provider) - { + withExclusiveControl(() -> { hotloader.interrupt(); - provider.close(); - } + getEngine().close(); + }); } @Override @@ -112,58 +150,40 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @Override public void hotload() { - J.aBukkit(this::hotloadBLOCKING); + withExclusiveControl(() -> getEngine().hotload()); } - public void hotloadBLOCKING() { - try { - hotloadLock.acquire(HOTLOAD_LOCKS); - initialize(); - hotloadLock.release(HOTLOAD_LOCKS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private void initialize() { - synchronized (provider) - { - provider.provideEngine(world, dimensionKey, dataLocation, isStudio(), (e) -> { - populators.clear(); - populators.add((BlockPopulator) e); - folder.checkIgnore(); - }); - } + public void withExclusiveControl(Runnable r) + { + J.a(() -> { + try { + loadLock.acquire(LOAD_LOCKS); + r.run(); + loadLock.release(LOAD_LOCKS); + } catch (Throwable e) { + e.printStackTrace(); + } + }); } @Override public @NotNull ChunkData generateChunkData(@NotNull World world, @NotNull Random ignored, int x, int z, @NotNull BiomeGrid biome) { try { - hotloadLock.acquire(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - try { - Iris.debug("Generated " + x + " " + z); + loadLock.acquire(); 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); getEngine().generate(x * 16, z * 16, blocks, biomes, true); - hotloadLock.release(); - return tc.getRaw(); - } - - catch(WrongEngineBroException e) - { - hotloadLock.release(); - hotloadBLOCKING(); - return generateChunkData(world, ignored, x, z, biome); + ChunkData c = tc.getRaw(); + Iris.debug("Generated " + x + " " + z); + loadLock.release(); + return c; } catch (Throwable e) { + loadLock.release(); Iris.error("======================================"); e.printStackTrace(); Iris.reportErrorChunk(x, z, e, "CHUNK"); @@ -177,10 +197,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } } - hotloadLock.release(); return d; } - } @NotNull diff --git a/src/main/java/com/volmit/iris/engine/platform/EngineProvider.java b/src/main/java/com/volmit/iris/engine/platform/EngineProvider.java deleted file mode 100644 index f4c7ad035..000000000 --- a/src/main/java/com/volmit/iris/engine/platform/EngineProvider.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.events.IrisEngineHotloadEvent; -import com.volmit.iris.core.project.loader.IrisData; -import com.volmit.iris.engine.IrisEngine; -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.parallel.MultiBurst; - -import java.io.File; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -public class EngineProvider { - private final AtomicReference> engine = new AtomicReference<>(); - - public void provideEngine(IrisWorld world, String dimension, File dataLocation, boolean studio, Consumer post) { - close(); - engine.set(MultiBurst.burst.completeValue(() -> { - IrisData data = IrisData.get(dataLocation); - IrisDimension realDimension = data.getDimensionLoader().load(dimension); - - if (realDimension == null) { - throw new RuntimeException("Cannot find dimension in " + data.getDataFolder().getAbsolutePath() + " with key " + dimension); - } - - EngineTarget target = new EngineTarget(world, realDimension, data); - Engine engine = new IrisEngine(target, studio); - post.accept(engine); - return engine; - })); - engine.get().whenComplete((e, x) -> Iris.callEvent(new IrisEngineHotloadEvent(e))); - } - - public Engine getEngine() { - try { - Engine e = engine.get().get(); - - if (e == null) { - throw new RuntimeException("NULL"); - } - - return e; - } catch (InterruptedException e) { - e.printStackTrace(); - throw new RuntimeException("INTERRUPTED"); - } catch (ExecutionException e) { - e.printStackTrace(); - throw new RuntimeException("EXECUTION ERROR"); - } - } - - public void close() { - if (engine.get() != null && engine.get().isDone()) { - Engine e = getEngine(); - - if (e != null) { - e.close(); - } - } - } -} diff --git a/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java b/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java index 8c12c414c..f3701efb2 100644 --- a/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java +++ b/src/main/java/com/volmit/iris/engine/platform/HeadlessGenerator.java @@ -23,9 +23,11 @@ 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; @@ -52,15 +54,13 @@ public class HeadlessGenerator implements PlatformChunkGenerator { private final HeadlessWorld world; private final NBTWorld writer; private final MultiBurst burst; - private final EngineProvider provider; + 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()); - provider = new EngineProvider(); - provider.provideEngine(world.getWorld(), world.getDimension().getLoadKey(), world.getDimension().getLoader().getDataFolder(), isStudio(), (e) -> { - }); + engine = new IrisEngine(new EngineTarget(world.getWorld(), world.getDimension(), world.getDimension().getLoader()), isStudio()); } @ChunkCoordinates @@ -132,7 +132,7 @@ public class HeadlessGenerator implements PlatformChunkGenerator { public void close() { burst.shutdownAndAwait(); - provider.close(); + getEngine().close(); writer.close(); } @@ -151,11 +151,6 @@ public class HeadlessGenerator implements PlatformChunkGenerator { return EMPTYPOINTS; } - @Override - public Engine getEngine() { - return provider.getEngine(); - } - @Override public boolean isHeadless() { return true; diff --git a/src/main/java/com/volmit/iris/util/context/IrisContext.java b/src/main/java/com/volmit/iris/util/context/IrisContext.java index 3795e955a..001b978fb 100644 --- a/src/main/java/com/volmit/iris/util/context/IrisContext.java +++ b/src/main/java/com/volmit/iris/util/context/IrisContext.java @@ -18,6 +18,7 @@ package com.volmit.iris.util.context; +import com.volmit.iris.Iris; import com.volmit.iris.core.project.loader.IrisData; import com.volmit.iris.engine.IrisComplex; import com.volmit.iris.engine.framework.Engine; @@ -30,7 +31,7 @@ import lombok.Data; @AllArgsConstructor public class IrisContext { private static ChronoLatch cl = new ChronoLatch(60000); - private static KMap context = new KMap<>(); + private static final KMap context = new KMap<>(); private final Engine engine; public static IrisContext get() { @@ -42,10 +43,22 @@ public class IrisContext { context.put(Thread.currentThread(), c); if (cl.flip()) { - for (Thread i : context.k()) { - if (!i.isAlive()) { - context.remove(i); + dereference(); + } + } + } + + public static void dereference() { + synchronized (context) + { + for (Thread i : context.k()) { + if (!i.isAlive() || context.get(i).engine.isClosed()) { + if(context.get(i).engine.isClosed()) + { + Iris.debug("Dereferenced Context " + i.getName() + " " + i.getId()); } + + context.remove(i); } } } diff --git a/src/main/java/com/volmit/iris/util/decree/handlers/ObjectHandler.java b/src/main/java/com/volmit/iris/util/decree/handlers/ObjectHandler.java index b4a8ce78a..fde7f0d8b 100644 --- a/src/main/java/com/volmit/iris/util/decree/handlers/ObjectHandler.java +++ b/src/main/java/com/volmit/iris/util/decree/handlers/ObjectHandler.java @@ -19,7 +19,7 @@ public class ObjectHandler implements DecreeParameterHandler { //noinspection ConstantConditions for (File i : Iris.instance.getDataFolder("packs").listFiles()) { if (i.isDirectory()) { - IrisData data = new IrisData(i, true); + IrisData data = IrisData.get(i); for (IrisObject j : data.getObjectLoader().loadAll(data.getObjectLoader().getPossibleKeys())) { p.putIfAbsent(j.getLoadKey(), j); } diff --git a/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 625bc64b1..afdae0de8 100644 --- a/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -192,7 +192,7 @@ public class Mantle { public synchronized void close() { Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); if (closed.get()) { - throw new RuntimeException("The Mantle is closed"); + return; } closed.set(true); diff --git a/src/main/java/com/volmit/iris/util/parallel/BurstExecutor.java b/src/main/java/com/volmit/iris/util/parallel/BurstExecutor.java index 9129a0c3f..642e15f6c 100644 --- a/src/main/java/com/volmit/iris/util/parallel/BurstExecutor.java +++ b/src/main/java/com/volmit/iris/util/parallel/BurstExecutor.java @@ -45,7 +45,7 @@ public class BurstExecutor { public BurstExecutor queue(List r) { synchronized (futures) { - for (Runnable i : r) { + for (Runnable i : new KList<>(r)) { CompletableFuture c = CompletableFuture.runAsync(i, executor); futures.add(c); } diff --git a/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index 03d677a8f..29440720a 100644 --- a/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -20,7 +20,7 @@ package com.volmit.iris.util.parallel; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.service.ExecutionSVC; +import com.volmit.iris.core.service.PreservationSVC; import com.volmit.iris.util.io.InstanceState; import com.volmit.iris.util.math.M; import com.volmit.iris.util.scheduling.J; @@ -71,7 +71,7 @@ public class MultiBurst { }; heartbeat.setName(name + " Monitor"); heartbeat.start(); - Iris.service(ExecutionSVC.class).register(this); + Iris.service(PreservationSVC.class).register(this); } private synchronized ExecutorService getService() { @@ -90,7 +90,7 @@ public class MultiBurst { return t; }); - Iris.service(ExecutionSVC.class).register(service); + Iris.service(PreservationSVC.class).register(service); Iris.debug("Started MultiBurst Pool " + name + " with " + tc + " threads at " + priority + " priority."); } diff --git a/src/main/java/com/volmit/iris/util/scheduling/Looper.java b/src/main/java/com/volmit/iris/util/scheduling/Looper.java index 6ae2822e0..60a74219b 100644 --- a/src/main/java/com/volmit/iris/util/scheduling/Looper.java +++ b/src/main/java/com/volmit/iris/util/scheduling/Looper.java @@ -19,14 +19,12 @@ package com.volmit.iris.util.scheduling; import com.volmit.iris.Iris; -import com.volmit.iris.core.service.ExecutionSVC; - -import java.util.concurrent.ExecutorService; +import com.volmit.iris.core.service.PreservationSVC; public abstract class Looper extends Thread { @SuppressWarnings("BusyWait") public void run() { - Iris.service(ExecutionSVC.class).register(this); + Iris.service(PreservationSVC.class).register(this); while (!interrupted()) { try { long m = loop(); diff --git a/src/main/java/com/volmit/iris/util/stream/arithmetic/AddingStream.java b/src/main/java/com/volmit/iris/util/stream/arithmetic/AddingStream.java index f9db98818..a6c0ef29b 100644 --- a/src/main/java/com/volmit/iris/util/stream/arithmetic/AddingStream.java +++ b/src/main/java/com/volmit/iris/util/stream/arithmetic/AddingStream.java @@ -58,5 +58,4 @@ public class AddingStream extends BasicStream { public T get(double x, double y, double z) { return fromDouble(add.apply(x, y, z) + getTypedSource().getDouble(x, y, z)); } - } diff --git a/src/main/java/com/volmit/iris/util/stream/interpolation/Interpolated.java b/src/main/java/com/volmit/iris/util/stream/interpolation/Interpolated.java index 3c334354a..7a50ffcf1 100644 --- a/src/main/java/com/volmit/iris/util/stream/interpolation/Interpolated.java +++ b/src/main/java/com/volmit/iris/util/stream/interpolation/Interpolated.java @@ -32,6 +32,7 @@ public interface Interpolated { Interpolated> CAVE_RESULTS = of((t) -> 0D, (t) -> null); Interpolated RNG = of((t) -> 0D, (t) -> null); Interpolated DOUBLE = of((t) -> t, (t) -> t); + Interpolated DOUBLE_ARRAY = of((t) -> 0D, (t) -> new Double[2]); Interpolated BOOLEAN = of((t) -> 0D, (t) -> false); Interpolated INT = of(Double::valueOf, Double::intValue); Interpolated LONG = of(Double::valueOf, Double::longValue);