diff --git a/src/main/java/com/volmit/iris/engine/IrisEngine.java b/src/main/java/com/volmit/iris/engine/IrisEngine.java index cff08766a..9dd992e77 100644 --- a/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -22,6 +22,7 @@ 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.engine.actuator.IrisBiomeActuator; import com.volmit.iris.engine.actuator.IrisDecorantActuator; import com.volmit.iris.engine.actuator.IrisTerrainIslandActuator; @@ -81,11 +82,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,58 +99,94 @@ 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) { - target.getData().dump(); 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() { + Iris.info("Hotload ENGINE"); + getData().dump(); + getData().clearLists(); + getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey())); + prehotload(); + setupEngine(); + Iris.callEvent(new IrisEngineHotloadEvent(this)); } @Override diff --git a/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index 070366de0..912bbc764 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(); } 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/platform/BukkitChunkGenerator.java b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 0500e0e87..05ed3e8f1 100644 --- a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -19,17 +19,22 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; +import com.volmit.iris.core.project.loader.IrisData; +import com.volmit.iris.engine.IrisEngine; import com.volmit.iris.engine.data.chunk.TerrainChunk; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.EngineTarget; import com.volmit.iris.engine.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; 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; @@ -44,9 +49,10 @@ 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 final Engine engine; private final IrisWorld world; private final File dataLocation; private final String dimensionKey; @@ -54,21 +60,20 @@ 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<>(); 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); + this.engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); + populators.add((BlockPopulator) engine); this.hotloader = new Looper() { @Override protected long loop() { @@ -84,13 +89,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 +96,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @Override public void close() { - synchronized (provider) - { - hotloader.interrupt(); - provider.close(); - } + hotloader.interrupt(); + getEngine().close(); } @Override @@ -112,38 +107,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @Override public void hotload() { - J.aBukkit(this::hotloadBLOCKING); - } - - 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(); - }); - } + getEngine().hotload(); + Iris.info("Hotload BKG"); } @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); PrecisionStopwatch ps = PrecisionStopwatch.start(); @@ -152,17 +121,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun 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); - } - catch (Throwable e) { Iris.error("======================================"); e.printStackTrace(); @@ -177,10 +138,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;