diff --git a/build.gradle.kts b/build.gradle.kts index 3c275b9ca..595224eaa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,13 +32,12 @@ plugins { java `java-library` alias(libs.plugins.shadow) - alias(libs.plugins.sentry) alias(libs.plugins.download) alias(libs.plugins.runPaper) } group = "com.volmit" -version = "3.7.0-1.20.1-1.21.7" +version = "3.7.2-1.20.1-1.21.8" apply() @@ -64,7 +63,7 @@ val serverMinHeap = "2G" val serverMaxHeap = "8G" //Valid values are: none, truecolor, indexed256, indexed16, indexed8 val color = "truecolor" -val errorReporting = false +val errorReporting = findProperty("errorReporting") as Boolean? ?: false val nmsBindings = mapOf( "v1_21_R5" to "1.21.7-R0.1-SNAPSHOT", @@ -110,7 +109,7 @@ nmsBindings.forEach { key, value -> pluginJars(tasks.jar.flatMap { it.archiveFile }) javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))} runDirectory.convention(layout.buildDirectory.dir("run/$key")) - systemProperty("disable.watchdog", "") + systemProperty("disable.watchdog", "true") systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("com.mojang.eula.agree", true) systemProperty("iris.suppressReporting", !errorReporting) @@ -176,12 +175,13 @@ tasks { group = "io.sentry" dependsOn("downloadCli") doLast { + val url = "http://sentry.volmit.com:8080" val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") - val org = "volmit-software" + val org = "sentry" val projectName = "iris" - exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) - exec(cli, "releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto", "--ignore-missing") - exec(cli, "releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) + exec(cli, "--url", url , "--auth-token", authToken, "releases", "new", "-o", org, "-p", projectName, version) + exec(cli, "--url", url , "--auth-token", authToken, "releases", "set-commits", "-o", org, "-p", projectName, version, "--auto", "--ignore-missing") + //exec(cli, "--url", url, "--auth-token", authToken, "releases", "finalize", "-o", org, "-p", projectName, version) cli.delete() } } @@ -298,4 +298,4 @@ fun registerCustomOutputTaskUnix(name: String, path: String) { into(file(path)) rename { "Iris.jar" } } -} \ No newline at end of file +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index a587f6c2a..3fa09a1c4 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -28,6 +28,8 @@ plugins { alias(libs.plugins.sentry) alias(libs.plugins.slimjar) alias(libs.plugins.grgit) + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.kotlin.lombok) } val apiVersion = "1.19" @@ -92,11 +94,23 @@ dependencies { slim(libs.zip) slim(libs.gson) slim(libs.asm) - slim(libs.bsf) - slim(libs.rhino) slim(libs.caffeine) slim(libs.byteBuddy.core) slim(libs.byteBuddy.agent) + slim(libs.dom4j) + slim(libs.jaxen) + + // Script Engine + slim(libs.kotlin.stdlib) + slim(libs.kotlin.coroutines) + slim(libs.kotlin.scripting.common) + slim(libs.kotlin.scripting.jvm) + slim(libs.kotlin.scripting.jvm.host) + slim(libs.kotlin.scripting.dependencies.maven) { + constraints { + slim(libs.mavenCore) + } + } } java { @@ -104,10 +118,11 @@ java { } sentry { + url = "http://sentry.volmit.com:8080/" autoInstallation.enabled = false includeSourceContext = true - org = "volmit-software" + org = "sentry" projectName = "iris" authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") } @@ -123,6 +138,13 @@ slimJar { relocate("net.kyori", "$lib.kyori") relocate("org.bstats", "$lib.metrics") relocate("io.sentry", "$lib.sentry") + relocate("org.apache.maven", "$lib.maven") + relocate("org.codehaus.plexus", "$lib.plexus") + relocate("org.eclipse.sisu", "$lib.sisu") + relocate("org.eclipse.aether", "$lib.aether") + relocate("com.google.inject", "$lib.guice") + relocate("org.dom4j", "$lib.dom4j") + relocate("org.jaxen", "$lib.jaxen") } tasks { diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index e9455150a..f49115e1d 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -100,6 +100,7 @@ public class Iris extends VolmitPlugin implements Listener { public static FileWatcher configWatcher; public static Platform platform; private static VolmitSender sender; + private static Thread shutdownHook; static { try { @@ -457,6 +458,7 @@ public class Iris extends VolmitPlugin implements Listener { configWatcher = new FileWatcher(getDataFile("settings.json")); services.values().forEach(IrisService::onEnable); services.values().forEach(this::registerListener); + addShutdownHook(); J.s(() -> { //J.a(IrisSafeguard::suggestPaper); //TODO reimplement this J.a(() -> IO.delete(getTemp())); @@ -475,6 +477,24 @@ public class Iris extends VolmitPlugin implements Listener { }); } + public void addShutdownHook() { + if (shutdownHook != null) { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } + shutdownHook = new Thread(() -> { + Bukkit.getWorlds() + .stream() + .map(IrisToolbelt::access) + .filter(Objects::nonNull) + .forEach(PlatformChunkGenerator::close); + + MultiBurst.burst.close(); + MultiBurst.ioBurst.close(); + services.clear(); + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); + } + public void checkForBukkitWorlds(Predicate filter) { try { IrisWorlds.readBukkitWorlds().forEach((s, generator) -> { @@ -555,17 +575,7 @@ public class Iris extends VolmitPlugin implements Listener { postShutdown.forEach(Runnable::run); super.onDisable(); - J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - Bukkit.getWorlds() - .stream() - .map(IrisToolbelt::access) - .filter(Objects::nonNull) - .forEach(PlatformChunkGenerator::close); - - MultiBurst.burst.close(); - services.clear(); - })); + J.attempt(new JarScanner(instance.getJarFile(), "", false)::scanAll); } private void setupPapi() { @@ -740,7 +750,7 @@ public class Iris extends VolmitPlugin implements Listener { String padd2 = Form.repeat(" ", 4); String[] info = {"", "", "", "", "", padd2 + C.IRIS + " Iris", padd2 + C.GRAY + " by " + "Volmit Software", padd2 + C.GRAY + " v" + C.IRIS + getDescription().getVersion()}; if (unstablemode) { - info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()}; + info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()}; } if (warningmode) { info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()}; diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 30216b29e..65991a686 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -49,11 +49,10 @@ public class IrisSettings { private IrisSettingsSentry sentry = new IrisSettingsSentry(); public static int getThreadCount(int c) { - return switch (c) { + return Math.max(switch (c) { case -1, -2, -4 -> Runtime.getRuntime().availableProcessors() / -c; - case 0, 1, 2 -> 1; default -> Math.max(c, 2); - }; + }, 1); } public static IrisSettings get() { @@ -138,6 +137,7 @@ public class IrisSettings { @Data public static class IrisSettingsConcurrency { public int parallelism = -1; + public int ioParallelism = -2; public int worldGenParallelism = -1; public int getWorldGenThreads() { @@ -176,13 +176,13 @@ public class IrisSettings { @Data public static class IrisSettingsUpdater { - public double threadMultiplier = 2; + public int maxConcurrency = 256; public double chunkLoadSensitivity = 0.7; public MsRange emptyMsRange = new MsRange(80, 100); public MsRange defaultMsRange = new MsRange(20, 40); - public double getThreadMultiplier() { - return Math.min(Math.abs(threadMultiplier), 0.1); + public int getMaxConcurrency() { + return Math.max(Math.abs(maxConcurrency), 1); } public double getChunkLoadSensitivity() { @@ -242,6 +242,8 @@ public class IrisSettings { public String defaultWorldType = "overworld"; public int maxBiomeChildDepth = 4; public boolean preventLeafDecay = true; + public boolean useMulticore = false; + public boolean offsetNoiseTypes = false; } @Data @@ -255,6 +257,7 @@ public class IrisSettings { @Data public static class IrisSettingsEngineSVC { public boolean useVirtualThreads = true; + public boolean forceMulticoreWrite = false; public int priority = Thread.NORM_PRIORITY; public int getPriority() { diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index e96eb29ed..06b961c13 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -37,6 +37,7 @@ import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.mantle.TectonicPlate; import com.volmit.iris.util.math.M; +import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.nbt.mca.MCAFile; import com.volmit.iris.util.nbt.mca.MCAUtil; import com.volmit.iris.util.parallel.MultiBurst; @@ -53,6 +54,7 @@ import org.bukkit.World; import java.io.*; import java.net.InetAddress; import java.net.NetworkInterface; +import java.nio.file.Files; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -64,7 +66,6 @@ import java.util.zip.GZIPOutputStream; public class CommandDeveloper implements DecreeExecutor { private CommandTurboPregen turboPregen; private CommandLazyPregen lazyPregen; - private CommandUpdater updater; @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) public void EngineStatus() { @@ -79,6 +80,33 @@ public class CommandDeveloper implements DecreeExecutor { Iris.reportError(new Exception("This is a test")); } + @Decree(description = "Test") + public void mantle(@Param(defaultValue = "false") boolean plate, @Param(defaultValue = "21474836474") String name) throws Throwable { + var base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin"); + var section = Iris.instance.getDataFile("dump", "pv." + name + ".section.bin"); + + //extractSection(base, section, 5604930, 4397); + + if (plate) { + try (var in = CountingDataInputStream.wrap(new BufferedInputStream(new FileInputStream(base)))) { + new TectonicPlate(1088, in, true); + } catch (Throwable e) { + e.printStackTrace(); + } + } else Matter.read(section); + if (!TectonicPlate.hasError()) + Iris.info("Read " + (plate ? base : section).length() + " bytes from " + (plate ? base : section).getAbsolutePath()); + } + + private void extractSection(File source, File target, long offset, int length) throws IOException { + var raf = new RandomAccessFile(source, "r"); + var bytes = new byte[length]; + raf.seek(offset); + raf.readFully(bytes); + raf.close(); + Files.write(target.toPath(), bytes); + } + @Decree(description = "Test") public void dumpThreads() { try { @@ -115,27 +143,6 @@ public class CommandDeveloper implements DecreeExecutor { } } - @Decree(description = "Test") - public void benchmarkMantle( - @Param(description = "The world to bench", aliases = {"world"}) - World world - ) throws IOException, ClassNotFoundException { - Engine engine = IrisToolbelt.access(world).getEngine(); - int maxHeight = engine.getTarget().getHeight(); - File folder = new File(Bukkit.getWorldContainer(), world.getName()); - int c = 0; - //MCAUtil.read() - - File tectonicplates = new File(folder, "mantle"); - for (File i : Objects.requireNonNull(tectonicplates.listFiles())) { - TectonicPlate.read(maxHeight, i, true); - c++; - Iris.info("Loaded count: " + c ); - - } - - } - @Decree(description = "Test") public void packBenchmark( @Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld") diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index 6ad54bae3..d8ef40989 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -21,7 +21,6 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.pregenerator.ChunkUpdater; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; @@ -33,7 +32,6 @@ import com.volmit.iris.util.decree.annotations.Decree; import com.volmit.iris.util.decree.annotations.Param; import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.format.Form; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.plugin.VolmitSender; @@ -55,6 +53,7 @@ import static org.bukkit.Bukkit.getServer; @Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") public class CommandIris implements DecreeExecutor { + private CommandUpdater updater; private CommandStudio studio; private CommandPregen pregen; private CommandSettings settings; @@ -312,24 +311,6 @@ public class CommandIris implements DecreeExecutor { return dir.delete(); } - @Decree(description = "Updates all chunk in the specified world") - public void updater( - @Param(description = "World to update chunks at") - World world - ) { - if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.GOLD + "This is not an Iris world"); - return; - } - ChunkUpdater updater = new ChunkUpdater(world); - if (sender().isPlayer()) { - sender().sendMessage(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks())); - } else { - Iris.info(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks())); - } - updater.start(); - } - @Decree(description = "Set aura spins") public void aura( @Param(description = "The h color value", defaultValue = "-20") diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index c062d0b64..319267e1b 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -47,7 +47,6 @@ import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONArray; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.MantleChunk; -import com.volmit.iris.util.mantle.MantleFlag; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; @@ -182,17 +181,32 @@ public class CommandStudio implements DecreeExecutor { int rad = engine.getMantle().getRadius(); var mantle = engine.getMantle().getMantle(); var chunkMap = new KMap(); - for (int i = -(radius + rad); i <= radius + rad; i++) { - for (int j = -(radius + rad); j <= radius + rad; j++) { - int xx = i + x, zz = j + z; - if (Math.abs(i) <= radius && Math.abs(j) <= radius) { - mantle.deleteChunk(xx, zz); - continue; + ParallelQueueJob prep = new ParallelQueueJob<>() { + @Override + public void execute(Position2 pos) { + var cpos = pos.add(x, z); + if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) { + mantle.deleteChunk(cpos.getX(), cpos.getZ()); + return; } - chunkMap.put(new Position2(xx, zz), mantle.getChunk(xx, zz)); - mantle.deleteChunk(xx, zz); + chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ())); + mantle.deleteChunk(cpos.getX(), cpos.getZ()); + } + + @Override + public String getName() { + return "Preparing Mantle"; + } + }; + for (int xx = -(radius + rad); xx <= radius + rad; xx++) { + for (int zz = -(radius + rad); zz <= radius + rad; zz++) { + prep.queue(new Position2(xx, zz)); } } + CountDownLatch pLatch = new CountDownLatch(1); + prep.execute(sender(), pLatch::countDown); + pLatch.await(); + ParallelQueueJob job = new ParallelQueueJob<>() { @Override @@ -210,23 +224,24 @@ public class CommandStudio implements DecreeExecutor { job.queue(new Position2(i + x, j + z)); } } - CountDownLatch latch = new CountDownLatch(1); job.execute(sender(), latch::countDown); latch.await(); int sections = mantle.getWorldHeight() >> 4; chunkMap.forEach((pos, chunk) -> { - var c = mantle.getChunk(pos.getX(), pos.getZ()); - for (MantleFlag flag : MantleFlag.values()) { - c.flag(flag, chunk.isFlagged(flag)); - } - c.clear(); - for (int y = 0; y < sections; y++) { - var slice = chunk.get(y); - if (slice == null) continue; - var s = c.getOrCreate(y); - slice.getSliceMap().forEach(s::putSlice); + var c = mantle.getChunk(pos.getX(), pos.getZ()).use(); + try { + c.copyFlags(chunk); + c.clear(); + for (int y = 0; y < sections; y++) { + var slice = chunk.get(y); + if (slice == null) continue; + var s = c.getOrCreate(y); + slice.getSliceMap().forEach(s::putSlice); + } + } finally { + c.release(); } }); } catch (Throwable e) { @@ -339,6 +354,42 @@ public class CommandStudio implements DecreeExecutor { }, null); } + @Decree(description = "Calculate the chance for each region to generate", origin = DecreeOrigin.PLAYER) + public void regions(@Param(description = "The radius in chunks", defaultValue = "500") int radius) { + var engine = engine(); + if (engine == null) { + sender().sendMessage(C.RED + "Only works in an Iris world!"); + return; + } + var sender = sender(); + var player = player(); + Thread.ofVirtual() + .start(() -> { + int d = radius * 2; + KMap data = new KMap<>(); + engine.getDimension().getRegions().forEach(key -> data.put(key, new AtomicInteger(0))); + var multiBurst = new MultiBurst("Region Sampler"); + var executor = multiBurst.burst(radius * radius); + sender.sendMessage(C.GRAY + "Generating data..."); + var loc = player.getLocation(); + int totalTasks = d * d; + AtomicInteger completedTasks = new AtomicInteger(0); + int c = J.ar(() -> sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding regions"), 0); + new Spiraler(d, d, (x, z) -> executor.queue(() -> { + var region = engine.getRegion((x << 4) + 8, (z << 4) + 8); + data.computeIfAbsent(region.getLoadKey(), (k) -> new AtomicInteger(0)) + .incrementAndGet(); + completedTasks.incrementAndGet(); + })).setOffset(loc.getBlockX(), loc.getBlockZ()).drain(); + executor.complete(); + multiBurst.close(); + J.car(c); + + sender.sendMessage(C.GREEN + "Done!"); + var loader = engine.getData().getRegionLoader(); + data.forEach((k, v) -> sender.sendMessage(C.GREEN + k + ": " + loader.load(k).getRarity() + " / " + Form.f((double) v.get() / totalTasks * 100, 2) + "%")); + }); + } @Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER) public void distances(@Param(description = "The radius in chunks") int radius) { @@ -350,7 +401,7 @@ public class CommandStudio implements DecreeExecutor { var sender = sender(); int d = radius * 2; KMap> data = new KMap<>(); - var multiBurst = new MultiBurst("Distance Sampler", Thread.MIN_PRIORITY); + var multiBurst = new MultiBurst("Distance Sampler"); var executor = multiBurst.burst(radius * radius); sender.sendMessage(C.GRAY + "Generating data..."); diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java b/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java index 38aba40a5..e19fc61ba 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java @@ -18,6 +18,7 @@ package com.volmit.iris.core.commands; +import lombok.Synchronized; import org.bukkit.World; import com.volmit.iris.Iris; @@ -32,7 +33,8 @@ import com.volmit.iris.util.format.Form; @Decree(name = "updater", origin = DecreeOrigin.BOTH, description = "Iris World Updater") public class CommandUpdater implements DecreeExecutor { - private ChunkUpdater chunkUpdater; + private final Object lock = new Object(); + private transient ChunkUpdater chunkUpdater; @Decree(description = "Updates all chunk in the specified world") public void start( @@ -43,19 +45,22 @@ public class CommandUpdater implements DecreeExecutor { sender().sendMessage(C.GOLD + "This is not an Iris world"); return; } - if (chunkUpdater != null) { - chunkUpdater.stop(); - } + synchronized (lock) { + if (chunkUpdater != null) { + chunkUpdater.stop(); + } - chunkUpdater = new ChunkUpdater(world); - if (sender().isPlayer()) { - sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); - } else { - Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); + chunkUpdater = new ChunkUpdater(world); + if (sender().isPlayer()) { + sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); + } else { + Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); + } + chunkUpdater.start(); } - chunkUpdater.start(); } + @Synchronized("lock") @Decree(description = "Pause the updater") public void pause( ) { if (chunkUpdater == null) { @@ -78,6 +83,7 @@ public class CommandUpdater implements DecreeExecutor { } } + @Synchronized("lock") @Decree(description = "Stops the updater") public void stop() { if (chunkUpdater == null) { diff --git a/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java b/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java index 6111b59d8..fa2332ff7 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java +++ b/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java @@ -30,7 +30,6 @@ import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; -import com.volmit.iris.util.data.registry.Attributes; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.math.BlockPosition; import com.volmit.iris.util.math.M; @@ -40,7 +39,6 @@ import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.O; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import org.bukkit.Location; -import org.bukkit.attribute.Attribute; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -415,8 +413,7 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener } private double getWorldX(double screenX) { - //return (mscale * screenX) + ((oxp / scale) * mscale); - return (mscale * screenX) + ((oxp / scale)); + return (mscale * screenX) + ((oxp / scale) * mscale); } private double getWorldZ(double screenZ) { @@ -486,6 +483,13 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener hz += Math.abs(hz - lz) * 0.36; } + if (Math.abs(lx - hx) < 0.5) { + hx = lx; + } + if (Math.abs(lz - hz) < 0.5) { + hz = lz; + } + if (centities.flip()) { J.s(() -> { synchronized (lastEntities) { @@ -510,8 +514,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener int iscale = (int) scale; g.setColor(Color.white); g.clearRect(0, 0, w, h); - int posX = (int) oxp; - int posZ = (int) ozp; + double offsetX = oxp / scale; + double offsetZ = ozp / scale; m.set(3); for (int r = 0; r < Math.max(w, h); r += iscale) { @@ -520,10 +524,14 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener int a = i - (w / 2); int b = j - (h / 2); if (a * a + b * b <= r * r) { - BufferedImage t = getTile(gg, iscale, Math.floorDiv((posX / iscale) + i, iscale) * iscale, Math.floorDiv((posZ / iscale) + j, iscale) * iscale, m); + int tx = (int) (Math.floor((offsetX + i) / iscale) * iscale); + int tz = (int) (Math.floor((offsetZ + j) / iscale) * iscale); + BufferedImage t = getTile(gg, iscale, tx, tz, m); if (t != null) { - g.drawImage(t, i - ((posX / iscale) % (iscale)), j - ((posZ / iscale) % (iscale)), iscale, iscale, (img, infoflags, x, y, width, height) -> true); + int rx = Math.floorMod((int) Math.floor(offsetX), iscale); + int rz = Math.floorMod((int) Math.floor(offsetZ), iscale); + g.drawImage(t, i - rx, j - rz, iscale, iscale, (img, infoflags, x, y, width, height) -> true); } } } @@ -651,8 +659,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener private void animateTo(double wx, double wz) { double cx = getWorldX(getWidth() / 2); double cz = getWorldZ(getHeight() / 2); - ox += (wx - cx); - oz += (wz - cz); + ox += ((wx - cx) / mscale) * scale; + oz += ((wz - cz) / mscale) * scale; } private void renderPosition(Graphics2D g, double x, double z) { @@ -807,11 +815,28 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener return; } - //Iris.info("Blocks/Pixel: " + (mscale) + ", Blocks Wide: " + (w * mscale)); + double m0 = mscale; + double m1 = m0 + ((0.25 * m0) * notches); + m1 = Math.max(m1, 0.00001); + if (m1 == m0) { + return; + } + positions.clear(); fastpositions.clear(); - mscale = mscale + ((0.25 * mscale) * notches); - mscale = Math.max(mscale, 0.00001); + + Point p = e.getPoint(); + double sx = p.getX(); + double sz = p.getY(); + + double newOxp = scale * ((m0 / m1) * (sx + (oxp / scale)) - sx); + double newOzp = scale * ((m0 / m1) * (sz + (ozp / scale)) - sz); + + mscale = m1; + oxp = newOxp; + ozp = newOzp; + ox = oxp; + oz = ozp; } @Override diff --git a/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java index bbc7e8088..d88e9f831 100644 --- a/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java @@ -1,6 +1,8 @@ package com.volmit.iris.core.link; import com.volmit.iris.core.link.data.DataType; +import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; @@ -22,7 +24,9 @@ import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; @Getter @@ -66,6 +70,18 @@ public abstract class ExternalDataProvider implements Listener { throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); } + /** + * Retrieves a list of all {@link BlockProperty} objects associated with the specified block identifier. + * + * @param blockId The identifier of the block whose properties are to be retrieved. Must not be null. + * @return A list of {@link BlockProperty} objects representing the properties of the block. + * @throws MissingResourceException If the specified block identifier is invalid or cannot be found. + */ + @NotNull + public List getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException { + return List.of(); + } + /** * @see ExternalDataProvider#getItemStack(Identifier) */ @@ -137,4 +153,18 @@ public abstract class ExternalDataProvider implements Listener { return new Pair<>(yaw, face); } + + protected static List YAW_FACE_BIOME_PROPERTIES = List.of( + BlockProperty.ofEnum(BiomeColor.class, "matchBiome", null), + BlockProperty.ofBoolean("randomYaw", false), + BlockProperty.ofFloat("yaw", 0, 0, 360f, false, true), + BlockProperty.ofBoolean("randomFace", true), + new BlockProperty( + "face", + BlockFace.class, + BlockFace.NORTH, + Arrays.asList(BlockFace.values()).subList(0, BlockFace.values().length - 1), + BlockFace::name + ) + ); } diff --git a/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java index f030fc6a8..5327578bd 100644 --- a/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java @@ -4,24 +4,27 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.link.ExternalDataProvider; import com.volmit.iris.core.link.Identifier; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.data.B; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.IrisCustomData; import dev.lone.itemsadder.api.CustomBlock; import dev.lone.itemsadder.api.CustomStack; +import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.event.EventHandler; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.List; import java.util.MissingResourceException; +import java.util.stream.Collectors; public class ItemAdderDataProvider extends ExternalDataProvider { - private KList itemNamespaces, blockNamespaces; + private final KSet itemNamespaces = new KSet<>(); + private final KSet blockNamespaces = new KSet<>(); public ItemAdderDataProvider() { super("ItemsAdder"); @@ -29,16 +32,12 @@ public class ItemAdderDataProvider extends ExternalDataProvider { @Override public void init() { - this.itemNamespaces = new KList<>(); - this.blockNamespaces = new KList<>(); + updateNamespaces(); + } - for (Identifier i : getTypes(DataType.ITEM)) { - itemNamespaces.addIfMissing(i.namespace()); - } - for (Identifier i : getTypes(DataType.BLOCK)) { - blockNamespaces.addIfMissing(i.namespace()); - Iris.info("Found ItemAdder Block: " + i); - } + @EventHandler + public void onLoadData(ItemsAdderLoadDataEvent event) { + updateNamespaces(); } @NotNull @@ -48,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider { if (block == null) { throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); } - return new IrisCustomData(B.getAir(), blockId); + return new IrisCustomData(block.getBaseBlockData(), blockId); } @NotNull @@ -81,9 +80,25 @@ public class ItemAdderDataProvider extends ExternalDataProvider { }; } + private void updateNamespaces() { + try { + updateNamespaces(DataType.ITEM); + updateNamespaces(DataType.BLOCK); + } catch (Throwable e) { + Iris.warn("Failed to update ItemAdder namespaces: " + e.getMessage()); + } + } + + private void updateNamespaces(DataType dataType) { + var namespaces = getTypes(dataType).stream().map(Identifier::namespace).collect(Collectors.toSet()); + var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces; + currentNamespaces.removeIf(n -> !namespaces.contains(n)); + currentNamespaces.addAll(namespaces); + } + @Override public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { if (dataType == DataType.ENTITY) return false; - return dataType == DataType.ITEM ? this.itemNamespaces.contains(id.namespace()) : this.blockNamespaces.contains(id.namespace()); + return dataType == DataType.ITEM ? itemNamespaces.contains(id.namespace()) : blockNamespaces.contains(id.namespace()); } } diff --git a/core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java index 4194f0c16..86d2ce327 100644 --- a/core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java @@ -23,6 +23,7 @@ import com.volmit.iris.core.link.ExternalDataProvider; import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KMap; @@ -41,6 +42,7 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; import java.util.Optional; @@ -77,6 +79,19 @@ public class MythicCrucibleDataProvider extends ExternalDataProvider { throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); } + @Override + public @NotNull List getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException { + CrucibleItem crucibleItem = this.itemManager.getItem(blockId.key()) + .orElseThrow(() -> new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key())); + + if (crucibleItem.getFurnitureData() != null) { + return YAW_FACE_BIOME_PROPERTIES; + } else if (crucibleItem.getBlockData() != null) { + return List.of(); + } + throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + } + @NotNull @Override public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { diff --git a/core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java index 7c08b8e6a..c43e0e282 100644 --- a/core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java @@ -17,6 +17,7 @@ import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; diff --git a/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java index 998fbf06f..a0029c320 100644 --- a/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java @@ -8,6 +8,7 @@ import com.volmit.iris.core.link.ExternalDataProvider; import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KMap; @@ -26,11 +27,8 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.List; import java.util.MissingResourceException; -import java.util.concurrent.atomic.AtomicBoolean; public class NexoDataProvider extends ExternalDataProvider { - private final AtomicBoolean failed = new AtomicBoolean(false); - public NexoDataProvider() { super("Nexo"); } @@ -59,6 +57,15 @@ public class NexoDataProvider extends ExternalDataProvider { throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); } + @Override + public @NotNull List getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException { + if (!NexoItems.exists(blockId.key())) { + throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + } + + return NexoFurniture.isFurniture(blockId.key()) ? YAW_FACE_BIOME_PROPERTIES : List.of(); + } + @NotNull @Override public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { @@ -66,7 +73,12 @@ public class NexoDataProvider extends ExternalDataProvider { if (builder == null) { throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); } - return builder.build(); + try { + return builder.build(); + } catch (Exception e) { + e.printStackTrace(); + throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + } } @Override @@ -125,9 +137,4 @@ public class NexoDataProvider extends ExternalDataProvider { if (dataType == DataType.ENTITY) return false; return "nexo".equalsIgnoreCase(id.namespace()); } - - @Override - public boolean isReady() { - return super.isReady() && !failed.get(); - } } diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index 00e02ac00..1bfcd1ba2 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -24,6 +24,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import com.volmit.iris.Iris; +import com.volmit.iris.core.scripting.environment.PackEnvironment; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.*; @@ -33,6 +34,8 @@ import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.format.C; +import com.volmit.iris.util.mantle.flag.MantleFlagAdapter; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.MultiBurst; @@ -54,6 +57,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { private static final KMap dataLoaders = new KMap<>(); private final File dataFolder; private final int id; + private final PackEnvironment environment; private boolean closed = false; private ResourceLoader biomeLoader; private ResourceLoader lootLoader; @@ -87,6 +91,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { this.engine = null; this.dataFolder = dataFolder; this.id = RNG.r.imax(); + this.environment = PackEnvironment.create(this); hotloaded(); } @@ -252,12 +257,20 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { } } - if (engine != null && t.getPreprocessors().isNotEmpty()) { + if (engine == null) return; + var global = engine.getDimension().getPreProcessors(t.getFolderName()); + var local = t.getPreprocessors(); + if ((global != null && global.isNotEmpty()) || local.isNotEmpty()) { synchronized (this) { - engine.getExecution().getAPI().setPreprocessorObject(t); + if (global != null) { + for (String i : global) { + engine.getExecution().preprocessObject(i, t); + Iris.debug("Loader<" + C.GREEN + t.getTypeName() + C.LIGHT_PURPLE + "> iprocess " + C.YELLOW + t.getLoadKey() + C.LIGHT_PURPLE + " in " + i); + } + } - for (String i : t.getPreprocessors()) { - engine.getExecution().execute(i); + for (String i : local) { + engine.getExecution().preprocessObject(i, t); Iris.debug("Loader<" + C.GREEN + t.getTypeName() + C.LIGHT_PURPLE + "> iprocess " + C.YELLOW + t.getLoadKey() + C.LIGHT_PURPLE + " in " + i); } } @@ -271,6 +284,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { public void close() { closed = true; dump(); + dataLoaders.remove(dataFolder); } public IrisData copy() { @@ -311,12 +325,14 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { } public synchronized void hotloaded() { + environment.close(); possibleSnippets = new KMap<>(); builder = new GsonBuilder() .addDeserializationExclusionStrategy(this) .addSerializationExclusionStrategy(this) .setLenient() .registerTypeAdapterFactory(this) + .registerTypeAdapter(MantleFlag.class, new MantleFlagAdapter()) .setPrettyPrinting(); loaders.clear(); File packs = dataFolder; @@ -344,6 +360,10 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter); gson = builder.create(); + dimensionLoader.streamAll() + .map(IrisDimension::getDataScripts) + .flatMap(KList::stream) + .forEach(environment::execute); } public void dump() { @@ -482,7 +502,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { } public void savePrefetch(Engine engine) { - BurstExecutor b = MultiBurst.burst.burst(loaders.size()); + BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size()); for (ResourceLoader i : loaders.values()) { b.queue(() -> { @@ -499,7 +519,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { } public void loadPrefetch(Engine engine) { - BurstExecutor b = MultiBurst.burst.burst(loaders.size()); + BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size()); for (ResourceLoader i : loaders.values()) { b.queue(() -> { diff --git a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java index a9b468e57..25d37642d 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java +++ b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java @@ -23,6 +23,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.project.SchemaBuilder; import com.volmit.iris.core.service.PreservationSVC; +import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.MeteredCache; import com.volmit.iris.util.collection.KList; @@ -44,10 +45,9 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import java.io.*; +import java.util.Arrays; import java.util.Locale; -import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -60,7 +60,7 @@ import java.util.zip.GZIPOutputStream; public class ResourceLoader implements MeteredCache { public static final AtomicDouble tlt = new AtomicDouble(0); private static final int CACHE_SIZE = 100000; - protected final AtomicReference> folderCache; + protected final AtomicCache> folderCache; protected KSet firstAccess; protected File root; protected String folderName; @@ -76,7 +76,7 @@ public class ResourceLoader implements MeteredCache { public ResourceLoader(File root, IrisData manager, String folderName, String resourceTypeName, Class objectClass) { this.manager = manager; firstAccess = new KSet<>(); - folderCache = new AtomicReference<>(); + folderCache = new AtomicCache<>(); sec = new ChronoLatch(5000); loads = new AtomicInteger(); this.objectClass = objectClass; @@ -216,6 +216,10 @@ public class ResourceLoader implements MeteredCache { return j; } + public Stream streamAll() { + return streamAll(Arrays.stream(getPossibleKeys())); + } + public Stream streamAll(Stream s) { return s.map(this::load); } @@ -236,7 +240,7 @@ public class ResourceLoader implements MeteredCache { public KList loadAllParallel(KList s) { KList m = new KList<>(); - BurstExecutor burst = MultiBurst.burst.burst(s.size()); + BurstExecutor burst = MultiBurst.ioBurst.burst(s.size()); for (String i : s) { burst.queue(() -> { @@ -361,29 +365,24 @@ public class ResourceLoader implements MeteredCache { } public KList getFolders() { - synchronized (folderCache) { - if (folderCache.get() == null) { - KList fc = new KList<>(); + return folderCache.aquire(() -> { + KList fc = new KList<>(); - File[] files = root.listFiles(); - if (files == null) { - throw new IllegalStateException("Failed to list files in " + root); - } + File[] files = root.listFiles(); + if (files == null) { + throw new IllegalStateException("Failed to list files in " + root); + } - for (File i : files) { - if (i.isDirectory()) { - if (i.getName().equals(folderName)) { - fc.add(i); - break; - } + for (File i : files) { + if (i.isDirectory()) { + if (i.getName().equals(folderName)) { + fc.add(i); + break; } } - - folderCache.set(fc); } - } - - return folderCache.get(); + return fc; + }); } public KList getFolders(String rc) { @@ -403,7 +402,7 @@ public class ResourceLoader implements MeteredCache { public void clearCache() { possibleKeys = null; loadCache.invalidate(); - folderCache.set(null); + folderCache.reset(); } public File fileFor(T b) { @@ -429,7 +428,7 @@ public class ResourceLoader implements MeteredCache { } public void clearList() { - folderCache.set(null); + folderCache.reset(); possibleKeys = null; } diff --git a/core/src/main/java/com/volmit/iris/core/loader/ScriptResourceLoader.java b/core/src/main/java/com/volmit/iris/core/loader/ScriptResourceLoader.java index 4c0a6a1de..793a0aa1b 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/ScriptResourceLoader.java +++ b/core/src/main/java/com/volmit/iris/core/loader/ScriptResourceLoader.java @@ -82,8 +82,8 @@ public class ScriptResourceLoader extends ResourceLoader { private Set getKeysInDirectory(File directory) { Set keys = new HashSet<>(); for (File file : directory.listFiles()) { - if (file.isFile() && file.getName().endsWith(".js")) { - keys.add(file.getName().replaceAll("\\Q.js\\E", "")); + if (file.isFile() && file.getName().endsWith(".kts")) { + keys.add(file.getName().replaceAll("\\Q.kts\\E", "")); } else if (file.isDirectory()) { keys.addAll(getKeysInDirectory(file)); } @@ -127,12 +127,12 @@ public class ScriptResourceLoader extends ResourceLoader { public File findFile(String name) { for (File i : getFolders(name)) { for (File j : i.listFiles()) { - if (j.isFile() && j.getName().endsWith(".js") && j.getName().split("\\Q.\\E")[0].equals(name)) { + if (j.isFile() && j.getName().endsWith(".kts") && j.getName().split("\\Q.\\E")[0].equals(name)) { return j; } } - File file = new File(i, name + ".js"); + File file = new File(i, name + ".kts"); if (file.exists()) { return file; @@ -147,12 +147,12 @@ public class ScriptResourceLoader extends ResourceLoader { private IrisScript loadRaw(String name) { for (File i : getFolders(name)) { for (File j : i.listFiles()) { - if (j.isFile() && j.getName().endsWith(".js") && j.getName().split("\\Q.\\E")[0].equals(name)) { + if (j.isFile() && j.getName().endsWith(".kts") && j.getName().split("\\Q.\\E")[0].equals(name)) { return loadFile(j, name); } } - File file = new File(i, name + ".js"); + File file = new File(i, name + ".kts"); if (file.exists()) { return loadFile(file, name); diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index ccc27d72d..c27b30cc8 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -20,6 +20,7 @@ package com.volmit.iris.core.nms; import com.volmit.iris.core.link.FoliaWorldsLink; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; @@ -37,10 +38,10 @@ import org.bukkit.entity.EntityType; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; import java.awt.Color; import java.util.concurrent.CompletableFuture; +import java.util.List; public interface INMSBinding { boolean hasTile(Material material); @@ -144,4 +145,6 @@ public interface INMSBinding { default boolean injectBukkit() { return true; } + + KMap> getBlockProperties(); } diff --git a/core/src/main/java/com/volmit/iris/core/nms/container/BlockProperty.java b/core/src/main/java/com/volmit/iris/core/nms/container/BlockProperty.java new file mode 100644 index 000000000..e10d65472 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/container/BlockProperty.java @@ -0,0 +1,154 @@ +package com.volmit.iris.core.nms.container; + +import com.volmit.iris.util.json.JSONArray; +import com.volmit.iris.util.json.JSONObject; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Function; + +public class BlockProperty { + private static final Set> NATIVES = Set.of(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, String.class); + private final String name; + private final Class type; + + private final Object defaultValue; + private final Set values; + private final Function nameFunction; + private final Function jsonFunction; + + public > BlockProperty( + String name, + Class type, + T defaultValue, + Collection values, + Function nameFunction + ) { + this.name = name; + this.type = type; + this.defaultValue = defaultValue; + this.values = Collections.unmodifiableSet(new TreeSet<>(values)); + this.nameFunction = (Function) (Object) nameFunction; + jsonFunction = NATIVES.contains(type) ? Function.identity() : this.nameFunction::apply; + } + + public static > BlockProperty ofEnum(Class type, String name, T defaultValue) { + return new BlockProperty( + name, + type, + defaultValue, + Arrays.asList(type.getEnumConstants()), + val -> val == null ? "null" : val.name() + ); + } + + public static BlockProperty ofFloat(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) { + return new BoundedDouble( + name, + defaultValue, + min, + max, + exclusiveMin, + exclusiveMax, + (f) -> String.format("%.2f", f) + ); + } + + public static BlockProperty ofBoolean(String name, boolean defaultValue) { + return new BlockProperty( + name, + Boolean.class, + defaultValue, + List.of(true, false), + (b) -> b ? "true" : "false" + ); + } + + @Override + public @NotNull String toString() { + return name + "=" + nameFunction.apply(defaultValue) + " [" + String.join(",", names()) + "]"; + } + + public String name() { + return name; + } + + public String defaultValue() { + return nameFunction.apply(defaultValue); + } + + public List names() { + return values.stream().map(nameFunction).toList(); + } + + public Object defaultValueAsJson() { + return jsonFunction.apply(defaultValue); + } + + public JSONArray valuesAsJson() { + return new JSONArray(values.stream().map(jsonFunction).toList()); + } + + public JSONObject buildJson() { + var json = new JSONObject(); + json.put("type", jsonType()); + json.put("default", defaultValueAsJson()); + if (!values.isEmpty()) json.put("enum", valuesAsJson()); + return json; + } + + public String jsonType() { + if (type == Boolean.class) + return "boolean"; + if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class) + return "integer"; + if (type == Float.class || type == Double.class) + return "number"; + return "string"; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (BlockProperty) obj; + return Objects.equals(this.name, that.name) && + Objects.equals(this.values, that.values) && + Objects.equals(this.type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(name, values, type); + } + + private static class BoundedDouble extends BlockProperty { + private final double min, max; + private final boolean exclusiveMin, exclusiveMax; + + public BoundedDouble( + String name, + double defaultValue, + double min, + double max, + boolean exclusiveMin, + boolean exclusiveMax, + Function nameFunction + ) { + super(name, Double.class, defaultValue, List.of(), nameFunction); + this.min = min; + this.max = max; + this.exclusiveMin = exclusiveMin; + this.exclusiveMax = exclusiveMax; + } + + @Override + public JSONObject buildJson() { + return super.buildJson() + .put("minimum", min) + .put("maximum", max) + .put("exclusiveMinimum", exclusiveMin) + .put("exclusiveMaximum", exclusiveMax); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index e180ab8d2..4c13db4f4 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.nms.v1X; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; @@ -40,6 +41,7 @@ import org.bukkit.generator.structure.Structure; import org.bukkit.inventory.ItemStack; import java.awt.Color; +import java.util.List; import java.util.stream.StreamSupport; public class NMSBinding1X implements INMSBinding { @@ -124,6 +126,15 @@ public class NMSBinding1X implements INMSBinding { return false; } + @Override + public KMap> getBlockProperties() { + KMap> map = new KMap<>(); + for (Material m : Material.values()) { + if (m.isBlock()) map.put(m, List.of()); + } + return map; + } + @Override public CompoundTag serializeEntity(Entity location) { return null; diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java index de5a767e2..d86bebcd5 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java @@ -8,7 +8,7 @@ import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RollingSequence; @@ -38,14 +38,14 @@ public class ChunkUpdater { private final AtomicInteger chunksUpdated = new AtomicInteger(); private final AtomicBoolean serverEmpty = new AtomicBoolean(true); private final AtomicLong lastCpsTime = new AtomicLong(M.ms()); - private final int coreLimit = (int) Math.max(Runtime.getRuntime().availableProcessors() * IrisSettings.get().getUpdater().getThreadMultiplier(), 1); - private final Semaphore semaphore = new Semaphore(256); - private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, 256, IrisSettings.get().getUpdater().emptyMsRange); + private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency(); + private final Semaphore semaphore = new Semaphore(maxConcurrency); + private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange); private final AtomicLong startTime = new AtomicLong(); private final Dimensions dimensions; private final PregenTask task; - private final ExecutorService executor = Executors.newFixedThreadPool(coreLimit); - private final ExecutorService chunkExecutor = Executors.newFixedThreadPool(coreLimit); + private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor(); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private final CountDownLatch latch; private final Engine engine; @@ -137,10 +137,10 @@ public class ChunkUpdater { loadBalancer.close(); semaphore.acquire(256); - executor.shutdown(); - executor.awaitTermination(5, TimeUnit.SECONDS); chunkExecutor.shutdown(); chunkExecutor.awaitTermination(5, TimeUnit.SECONDS); + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); scheduler.shutdownNow(); unloadAndSaveAllChunks(); } catch (Exception ignored) {} diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java index b9a3de943..d124b25bc 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java @@ -3,7 +3,9 @@ package com.volmit.iris.core.pregenerator.cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.RemovalCause; +import com.github.benmanes.caffeine.cache.Scheduler; import com.volmit.iris.Iris; +import com.volmit.iris.util.data.KCache; import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.RegionCoordinates; @@ -14,12 +16,10 @@ import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; import org.jetbrains.annotations.Nullable; -import javax.annotation.concurrent.NotThreadSafe; import java.io.*; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -@NotThreadSafe @RequiredArgsConstructor class PregenCacheImpl implements PregenCache { private static final int SIZE = 32; @@ -27,6 +27,8 @@ class PregenCacheImpl implements PregenCache { private final HyperLock hyperLock = new HyperLock(SIZE * 2, true); private final LoadingCache cache = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.SECONDS) + .executor(KCache.EXECUTOR) + .scheduler(Scheduler.systemScheduler()) .maximumSize(SIZE) .removalListener(this::onRemoval) .evictionListener(this::onRemoval) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 988b16ee6..a32d7a523 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -190,7 +190,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { private class ServiceExecutor implements Executor { private final ExecutorService service = IrisSettings.get().getPregen().isUseVirtualThreads() ? Executors.newVirtualThreadPerTaskExecutor() : - new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); + new MultiBurst("Iris Async Pregen"); public void generate(int x, int z, PregenListener listener) { service.submit(() -> { diff --git a/core/src/main/java/com/volmit/iris/core/project/Gradle.java b/core/src/main/java/com/volmit/iris/core/project/Gradle.java new file mode 100644 index 000000000..c28b28ca4 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/project/Gradle.java @@ -0,0 +1,104 @@ +package com.volmit.iris.core.project; + +import com.volmit.iris.Iris; +import com.volmit.iris.util.io.IO; +import org.zeroturnaround.zip.ZipUtil; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.HashMap; +import java.util.Optional; +import java.util.Scanner; + +public class Gradle { + private static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().contains("win"); + private static final String[] ENVIRONMENT = createEnvironment(); + private static final String VERSION = "8.14.2"; + private static final String DISTRIBUTION_URL = "https://services.gradle.org/distributions/gradle-" + VERSION + "-bin.zip"; + private static final String HASH = IO.hash(DISTRIBUTION_URL); + + public static synchronized void wrapper(File projectDir) { + try { + File settings = new File(projectDir, "settings.gradle.kts"); + if (!settings.exists()) settings.createNewFile(); + runGradle(projectDir, "wrapper"); + } catch (Throwable e) { + Iris.error("Failed to install gradle wrapper!"); + e.printStackTrace(); + Iris.reportError(e); + } + } + + public static void runGradle(File projectDir, String... args) throws IOException, InterruptedException { + File gradle = downloadGradle(false); + String[] cmd = new String[args.length + 1]; + cmd[0] = gradle.getAbsolutePath(); + System.arraycopy(args, 0, cmd, 1, args.length); + var process = Runtime.getRuntime().exec(cmd, ENVIRONMENT, projectDir); + attach(process.getInputStream()); + attach(process.getErrorStream()); + var code = process.waitFor(); + if (code == 0) return; + throw new RuntimeException("Gradle exited with code " + code); + } + + private static synchronized File downloadGradle(boolean force) { + var folder = Iris.instance.getDataFolder("cache", HASH.substring(0, 2), HASH); + if (force) { + IO.delete(folder); + folder.mkdirs(); + } + + var bin = new File(folder, "gradle-" + VERSION + "/bin/gradle" + (WINDOWS ? ".bat" : "")); + if (bin.exists()) { + bin.setExecutable(true); + return bin; + } + + try (var input = new BufferedInputStream(URI.create(DISTRIBUTION_URL).toURL().openStream())) { + ZipUtil.unpack(input, folder); + } catch (Throwable e) { + throw new RuntimeException("Failed to download gradle", e); + } + + bin.setExecutable(true); + return bin; + } + + private static String[] createEnvironment() { + var env = new HashMap<>(System.getenv()); + env.put("JAVA_HOME", findJavaHome()); + return env.entrySet() + .stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .toArray(String[]::new); + } + + private static String findJavaHome() { + String javaHome = System.getProperty("java.home"); + if (javaHome != null && new File(javaHome + "/bin/java" + (WINDOWS ? ".exe" : "")).exists()) { + return javaHome; + } + + return ProcessHandle.current() + .info() + .command() + .map(s -> new File(s).getAbsoluteFile().getParentFile().getParentFile()) + .flatMap(f -> f.exists() ? Optional.of(f.getAbsolutePath()) : Optional.empty()) + .orElseThrow(() -> new RuntimeException("Failed to find java home, please set java.home system property")); + } + + private static void attach(InputStream stream) { + Thread.ofVirtual().start(() -> { + try (var in = new Scanner(stream)) { + while (in.hasNextLine()) { + String line = in.nextLine(); + Iris.debug("[GRADLE] " + line); + } + } + }); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/project/IrisProject.java b/core/src/main/java/com/volmit/iris/core/project/IrisProject.java index c0f327b6f..716173bd6 100644 --- a/core/src/main/java/com/volmit/iris/core/project/IrisProject.java +++ b/core/src/main/java/com/volmit/iris/core/project/IrisProject.java @@ -24,6 +24,7 @@ import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.ResourceLoader; +import com.volmit.iris.core.scripting.environment.SimpleEnvironment; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.object.*; import com.volmit.iris.engine.object.annotations.Snippet; @@ -49,6 +50,8 @@ import lombok.Data; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.World; +import org.dom4j.Document; +import org.dom4j.Element; import org.zeroturnaround.zip.ZipUtil; import java.awt.*; @@ -217,24 +220,15 @@ public class IrisProject { close(); } - boolean hasError = false; - - if (hasError) { - return; - } - - IrisDimension d = IrisData.loadAnyDimension(getName()); - if (d == null) { - sender.sendMessage("Can't find dimension: " + getName()); - return; - } else if (sender.isPlayer()) { - sender.player().setGameMode(GameMode.SPECTATOR); - } - - openVSCode(sender); - - J.a(() -> { + IrisDimension d = IrisData.loadAnyDimension(getName()); + if (d == null) { + sender.sendMessage("Can't find dimension: " + getName()); + return; + } else if (sender.isPlayer()) { + sender.player().setGameMode(GameMode.SPECTATOR); + } + try { activeProvider = (PlatformChunkGenerator) IrisToolbelt.createWorld() .seed(seed) @@ -247,6 +241,8 @@ public class IrisProject { } catch (IrisException e) { e.printStackTrace(); } + + openVSCode(sender); }); } @@ -359,6 +355,74 @@ public class IrisProject { settings.put("json.schemas", schemas); ws.put("settings", settings); + dm.getEnvironment().configureProject(); + File schemasFile = new File(path, ".idea" + File.separator + "jsonSchemas.xml"); + Document doc = IO.read(schemasFile); + Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']"); + if (mappings == null) { + mappings = doc.getRootElement() + .addElement("component") + .addAttribute("name", "JsonSchemaMappingsProjectConfiguration"); + } + + Element state = (Element) mappings.selectSingleNode("state"); + if (state == null) state = mappings.addElement("state"); + + Element map = (Element) state.selectSingleNode("map"); + if (map == null) map = state.addElement("map"); + var schemaMap = new KMap(); + schemas.forEach(element -> { + if (!(element instanceof JSONObject obj)) + return; + + String url = obj.getString("url"); + String dir = obj.getJSONArray("fileMatch").getString(0); + schemaMap.put(url, dir.substring(1, dir.indexOf("/*"))); + }); + + map.selectNodes("entry/value/SchemaInfo/option[@name='relativePathToSchema']") + .stream() + .map(node -> node.valueOf("@value")) + .forEach(schemaMap::remove); + + var ideaSchemas = map; + schemaMap.forEach((url, dir) -> { + var genName = UUID.randomUUID().toString(); + + var info = ideaSchemas.addElement("entry") + .addAttribute("key", genName) + .addElement("value") + .addElement("SchemaInfo"); + info.addElement("option") + .addAttribute("name", "generatedName") + .addAttribute("value", genName); + info.addElement("option") + .addAttribute("name", "name") + .addAttribute("value", dir); + info.addElement("option") + .addAttribute("name", "relativePathToSchema") + .addAttribute("value", url); + + + var item = info.addElement("option") + .addAttribute("name", "patterns") + .addElement("list") + .addElement("Item"); + item.addElement("option") + .addAttribute("name", "directory") + .addAttribute("value", "true"); + item.addElement("option") + .addAttribute("name", "path") + .addAttribute("value", dir); + item.addElement("option") + .addAttribute("name", "mappingKind") + .addAttribute("value", "Directory"); + }); + if (!schemaMap.isEmpty()) { + IO.write(schemasFile, doc); + } + Gradle.wrapper(path); + return ws; } diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index f5fe814c3..7e61233f3 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -110,9 +110,12 @@ public class SchemaBuilder { private JSONObject buildProperties(Class c) { JSONObject o = new JSONObject(); JSONObject properties = new JSONObject(); - o.put("description", getDescription(c)); + String desc = getDescription(c); + o.put("description", desc); + o.put("x-intellij-html-description", desc.replace("\n", "
")); o.put("type", getType(c)); JSONArray required = new JSONArray(); + JSONArray extended = new JSONArray(); if (c.isAssignableFrom(IrisRegistrant.class) || IrisRegistrant.class.isAssignableFrom(c)) { for (Field k : IrisRegistrant.class.getDeclaredFields()) { @@ -124,11 +127,15 @@ public class SchemaBuilder { JSONObject property = buildProperty(k, c); - if (property.getBoolean("!required")) { + if (Boolean.TRUE == property.remove("!required")) { required.put(k.getName()); } - property.remove("!required"); + if (Boolean.TRUE == property.remove("!top")) { + extended.put(property); + continue; + } + properties.put(k.getName(), property); } } @@ -142,15 +149,24 @@ public class SchemaBuilder { JSONObject property = buildProperty(k, c); - if (property.getBoolean("!required")) + if (Boolean.TRUE == property.remove("!required")) { required.put(k.getName()); - property.remove("!required"); + } + + if (Boolean.TRUE == property.remove("!top")) { + extended.put(property); + continue; + } + properties.put(k.getName(), property); } if (required.length() > 0) { o.put("required", required); } + if (extended.length() > 0) { + o.put("allOf", extended); + } o.put("properties", properties); @@ -343,13 +359,63 @@ public class SchemaBuilder { } } case "object" -> { - fancyType = k.getType().getSimpleName().replaceAll("\\QIris\\E", "") + " (Object)"; - String key = "obj-" + k.getType().getCanonicalName().replaceAll("\\Q.\\E", "-").toLowerCase(); - if (!definitions.containsKey(key)) { - definitions.put(key, new JSONObject()); - definitions.put(key, buildProperties(k.getType())); + //TODO add back descriptions + if (k.isAnnotationPresent(RegistryMapBlockState.class)) { + String blockType = k.getDeclaredAnnotation(RegistryMapBlockState.class).value(); + fancyType = "Block State"; + prop.put("!top", true); + JSONArray any = new JSONArray(); + prop.put("anyOf", any); + + B.getBlockStates().forEach((blocks, properties) -> { + if (blocks.isEmpty()) return; + + String raw = blocks.getFirst().replace(':', '_'); + String enumKey = "enum-block-state-" + raw; + String propertiesKey = "obj-block-state-" + raw; + + any.put(new JSONObject() + .put("if", new JSONObject() + .put("properties", new JSONObject() + .put(blockType, new JSONObject() + .put("type", "string") + .put("$ref", "#/definitions/" + enumKey)))) + .put("then", new JSONObject() + .put("properties", new JSONObject() + .put(k.getName(), new JSONObject() + .put("type", "object") + .put("$ref", "#/definitions/" + propertiesKey)))) + .put("else", false)); + + if (!definitions.containsKey(enumKey)) { + JSONArray filters = new JSONArray(); + blocks.forEach(filters::put); + + definitions.put(enumKey, new JSONObject() + .put("type", "string") + .put("enum", filters)); + } + + if (!definitions.containsKey(propertiesKey)) { + JSONObject props = new JSONObject(); + properties.forEach(property -> { + props.put(property.name(), property.buildJson()); + }); + + definitions.put(propertiesKey, new JSONObject() + .put("type", "object") + .put("properties", props)); + } + }); + } else { + fancyType = k.getType().getSimpleName().replaceAll("\\QIris\\E", "") + " (Object)"; + String key = "obj-" + k.getType().getCanonicalName().replaceAll("\\Q.\\E", "-").toLowerCase(); + if (!definitions.containsKey(key)) { + definitions.put(key, new JSONObject()); + definitions.put(key, buildProperties(k.getType())); + } + prop.put("$ref", "#/definitions/" + key); } - prop.put("$ref", "#/definitions/" + key); } case "array" -> { fancyType = "List of Something...?"; @@ -520,11 +586,12 @@ public class SchemaBuilder { } KList d = new KList<>(); - d.add(k.getName()); - d.add(getFieldDescription(k)); - d.add(" "); - d.add(fancyType); - d.add(getDescription(k.getType())); + d.add("" + k.getName() + ""); + d.add(getFieldDescription(k) + "
"); + d.add("" + fancyType + ""); + String typeDesc = getDescription(k.getType()); + boolean present = !typeDesc.isBlank(); + if (present) d.add(typeDesc); Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class); if (snippet == null) { @@ -536,8 +603,9 @@ public class SchemaBuilder { if (snippet != null) { String sm = snippet.value(); - d.add(" "); + if (present) d.add(" "); d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here."); + present = false; } try { @@ -545,15 +613,13 @@ public class SchemaBuilder { Object value = k.get(cl.newInstance()); if (value != null) { + if (present) d.add(" "); if (value instanceof List) { - d.add(" "); - d.add("* Default Value is an empty list"); + d.add(SYMBOL_LIMIT__N + " Default Value is an empty list"); } else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !KeyedType.isKeyed(cl)) { - d.add(" "); - d.add("* Default Value is a default object (create this object to see default properties)"); + d.add(SYMBOL_LIMIT__N + " Default Value is a default object (create this object to see default properties)"); } else { - d.add(" "); - d.add("* Default Value is " + value); + d.add(SYMBOL_LIMIT__N + " Default Value is " + value); } } } catch (Throwable ignored) { @@ -561,8 +627,14 @@ public class SchemaBuilder { } description.forEach((g) -> d.add(g.trim())); + String desc = d.toString("\n") + .replace("
", "\n") + .replace("", "") + .replace("", ""); + String hDesc = d.toString("
"); prop.put("type", type); - prop.put("description", d.toString("\n")); + prop.put("description", desc); + prop.put("x-intellij-html-description", hDesc); return buildSnippet(prop, k.getType()); } @@ -588,8 +660,10 @@ public class SchemaBuilder { arr.put(prop); arr.put(str); str.put("description", prop.getString("description")); + str.put("x-intellij-html-description", prop.getString("x-intellij-html-description")); anyOf.put("anyOf", arr); anyOf.put("description", prop.getString("description")); + anyOf.put("x-intellij-html-description", prop.getString("x-intellij-html-description")); anyOf.put("!required", type.isAnnotationPresent(Required.class)); return anyOf; @@ -615,7 +689,9 @@ public class SchemaBuilder { String name = function.apply(gg); j.put("const", name); Desc dd = type.getField(name).getAnnotation(Desc.class); - j.put("description", dd == null ? ("No Description for " + name) : dd.value()); + String desc = dd == null ? ("No Description for " + name) : dd.value(); + j.put("description", desc); + j.put("x-intellij-html-description", desc.replace("\n", "
")); a.put(j); } catch (Throwable e) { Iris.reportError(e); diff --git a/core/src/main/java/com/volmit/iris/core/scripting/environment/EngineEnvironment.java b/core/src/main/java/com/volmit/iris/core/scripting/environment/EngineEnvironment.java new file mode 100644 index 000000000..64507eeb8 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/scripting/environment/EngineEnvironment.java @@ -0,0 +1,25 @@ +package com.volmit.iris.core.scripting.environment; + +import com.volmit.iris.core.loader.IrisRegistrant; +import com.volmit.iris.core.scripting.kotlin.environment.IrisExecutionEnvironment; +import com.volmit.iris.engine.framework.Engine; +import lombok.NonNull; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.Nullable; + +public interface EngineEnvironment extends PackEnvironment { + static EngineEnvironment create(@NonNull Engine engine) { + return new IrisExecutionEnvironment(engine); + } + + @NonNull + Engine getEngine(); + + @Nullable + Object spawnMob(@NonNull String script, @NonNull Location location); + + void postSpawnMob(@NonNull String script, @NonNull Location location, @NonNull Entity mob); + + void preprocessObject(@NonNull String script, @NonNull IrisRegistrant object); +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/core/scripting/environment/PackEnvironment.java b/core/src/main/java/com/volmit/iris/core/scripting/environment/PackEnvironment.java new file mode 100644 index 000000000..c5dbccf22 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/scripting/environment/PackEnvironment.java @@ -0,0 +1,19 @@ +package com.volmit.iris.core.scripting.environment; + +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment; +import com.volmit.iris.util.math.RNG; +import lombok.NonNull; +import org.jetbrains.annotations.Nullable; + +public interface PackEnvironment extends SimpleEnvironment { + static PackEnvironment create(@NonNull IrisData data) { + return new IrisPackExecutionEnvironment(data); + } + + @NonNull + IrisData getData(); + + @Nullable + Object createNoise(@NonNull String script, @NonNull RNG rng); +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/core/scripting/environment/SimpleEnvironment.java b/core/src/main/java/com/volmit/iris/core/scripting/environment/SimpleEnvironment.java new file mode 100644 index 000000000..4eb7e0ce8 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/scripting/environment/SimpleEnvironment.java @@ -0,0 +1,34 @@ +package com.volmit.iris.core.scripting.environment; + +import com.volmit.iris.core.scripting.kotlin.environment.IrisSimpleExecutionEnvironment; +import lombok.NonNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.Map; + +public interface SimpleEnvironment { + static SimpleEnvironment create() { + return new IrisSimpleExecutionEnvironment(); + } + + static SimpleEnvironment create(@NonNull File projectDir) { + return new IrisSimpleExecutionEnvironment(projectDir); + } + + void configureProject(); + + void execute(@NonNull String script); + + void execute(@NonNull String script, @NonNull Class type, @Nullable Map<@NonNull String, Object> vars); + + @Nullable + Object evaluate(@NonNull String script); + + @Nullable + Object evaluate(@NonNull String script, @NonNull Class type, @Nullable Map<@NonNull String, Object> vars); + + default void close() { + + } +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/core/scripting/func/BiomeLookup.java b/core/src/main/java/com/volmit/iris/core/scripting/func/BiomeLookup.java new file mode 100644 index 000000000..d27247b1a --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/scripting/func/BiomeLookup.java @@ -0,0 +1,10 @@ +package com.volmit.iris.core.scripting.func; + +import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.util.documentation.BlockCoordinates; + +@FunctionalInterface +public interface BiomeLookup { + @BlockCoordinates + IrisBiome at(int x, int z); +} diff --git a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java index 49303b60c..528e41869 100644 --- a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.service; import com.volmit.iris.Iris; import com.volmit.iris.core.link.*; import com.volmit.iris.core.link.data.DataType; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; @@ -69,8 +70,8 @@ public class ExternalDataSVC implements IrisService { @EventHandler public void onPluginEnable(PluginEnableEvent e) { - if (activeProviders.stream().noneMatch(p -> p.getPlugin().equals(e.getPlugin()))) { - providers.stream().filter(p -> p.isReady() && p.getPlugin().equals(e.getPlugin())).findFirst().ifPresent(edp -> { + if (activeProviders.stream().noneMatch(p -> e.getPlugin().equals(p.getPlugin()))) { + providers.stream().filter(p -> p.isReady() && e.getPlugin().equals(p.getPlugin())).findFirst().ifPresent(edp -> { activeProviders.add(edp); edp.init(); Iris.instance.registerListener(edp); @@ -107,6 +108,18 @@ public class ExternalDataSVC implements IrisService { } } + public Optional> getBlockProperties(final Identifier key) { + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, DataType.BLOCK)).findFirst(); + if (provider.isEmpty()) + return Optional.empty(); + try { + return Optional.of(provider.get().getBlockProperties(key)); + } catch (MissingResourceException e) { + Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]"); + return Optional.empty(); + } + } + public Optional getItemStack(Identifier key, KMap customNbt) { Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, DataType.ITEM)).findFirst(); if (provider.isEmpty()) { @@ -150,6 +163,14 @@ public class ExternalDataSVC implements IrisService { .toList(); } + public Collection>> getAllBlockProperties() { + return activeProviders.stream() + .flatMap(p -> p.getTypes(DataType.BLOCK) + .stream() + .map(id -> new Pair<>(id, p.getBlockProperties(id)))) + .toList(); + } + public static Pair> parseState(Identifier key) { if (!key.key().contains("[") || !key.key().contains("]")) { return new Pair<>(key, new KMap<>()); diff --git a/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java index e8c194974..ceeedbee7 100644 --- a/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java @@ -2,9 +2,11 @@ package com.volmit.iris.core.service; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.Scheduler; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.pregenerator.cache.PregenCache; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.data.KCache; import com.volmit.iris.util.plugin.IrisService; import lombok.NonNull; import org.bukkit.Bukkit; @@ -20,7 +22,11 @@ import java.io.File; import java.util.function.Function; public class GlobalCacheSVC implements IrisService { - private static final Cache REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build(); + private static final Cache REFERENCE_CACHE = Caffeine.newBuilder() + .executor(KCache.EXECUTOR) + .scheduler(Scheduler.systemScheduler()) + .weakValues() + .build(); private final KMap globalCache = new KMap<>(); private transient boolean lastState; private static boolean disabled = true; diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java index df3951ea1..a9b31244d 100644 --- a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -157,6 +157,7 @@ public class IrisEngineSVC implements IrisService { private final class Registered { private final String name; private final PlatformChunkGenerator access; + private final int offset = RNG.r.nextInt(1000); private transient ScheduledFuture trimmer; private transient ScheduledFuture unloader; private transient boolean closed; @@ -193,7 +194,7 @@ public class IrisEngineSVC implements IrisService { Iris.error("EngineSVC: Failed to trim for " + name); e.printStackTrace(); } - }, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS); + }, offset, 2000, TimeUnit.MILLISECONDS); } if (unloader == null || unloader.isDone() || unloader.isCancelled()) { @@ -204,7 +205,7 @@ public class IrisEngineSVC implements IrisService { try { long unloadStart = System.currentTimeMillis(); - int count = engine.getMantle().unloadTectonicPlate(tectonicLimit()); + int count = engine.getMantle().unloadTectonicPlate(IrisSettings.get().getPerformance().getEngineSVC().forceMulticoreWrite ? 0 : tectonicLimit()); if (count > 0) { Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); } @@ -213,7 +214,7 @@ public class IrisEngineSVC implements IrisService { Iris.error("EngineSVC: Failed to unload for " + name); e.printStackTrace(); } - }, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS); + }, offset + 1000, 2000, TimeUnit.MILLISECONDS); } } diff --git a/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java b/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java index b43e3ee95..317e1731e 100644 --- a/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java @@ -43,10 +43,6 @@ public class PreservationSVC implements IrisService { threads.add(t); } - public void register(MultiBurst burst) { - - } - public void register(ExecutorService service) { services.add(service); } diff --git a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java index 70422c6a1..9435dc822 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java @@ -25,8 +25,6 @@ import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; 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.collection.KSet; import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.data.DataProvider; import com.volmit.iris.util.interpolation.IrisInterpolation.NoiseKey; @@ -43,7 +41,7 @@ import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; import java.io.File; -import java.util.UUID; +import java.util.*; @Data @EqualsAndHashCode(exclude = "data") @@ -53,7 +51,7 @@ public class IrisComplex implements DataProvider { private RNG rng; private double fluidHeight; private IrisData data; - private KMap> generators; + private Map> generators; private ProceduralStream regionStream; private ProceduralStream regionStyleStream; private ProceduralStream regionIdentityStream; @@ -98,10 +96,10 @@ public class IrisComplex implements DataProvider { this.data = engine.getData(); double height = engine.getMaxHeight(); fluidHeight = engine.getDimension().getFluidHeight(); - generators = new KMap<>(); + generators = new HashMap<>(); focusBiome = engine.getFocus(); focusRegion = engine.getFocusRegion(); - KMap> inferredStreams = new KMap<>(); + Map> inferredStreams = new HashMap<>(); if (focusBiome != null) { focusBiome.setInferredType(InferredType.LAND); @@ -118,6 +116,7 @@ public class IrisComplex implements DataProvider { .getAllBiomes(this) .forEach(this::registerGenerators)); } + boolean legacy = engine.getDimension().isLegacyRarity(); overlayStream = ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream"); engine.getDimension().getOverlayNoise().forEach(i -> overlayStream = overlayStream.add((x, z) -> i.get(rng, getData(), x, z))); rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream() @@ -131,7 +130,7 @@ public class IrisComplex implements DataProvider { ProceduralStream.of((x, z) -> focusRegion, Interpolated.of(a -> 0D, a -> focusRegion)) : regionStyleStream - .selectRarity(data.getRegionLoader().loadAll(engine.getDimension().getRegions())) + .selectRarity(data.getRegionLoader().loadAll(engine.getDimension().getRegions()), legacy) .cache2D("regionStream", engine, cacheSize).waste("Region Stream"); regionIDStream = regionIdentityStream.convertCached((i) -> new UUID(Double.doubleToLongBits(i), String.valueOf(i * 38445).hashCode() * 3245556666L)).waste("Region ID Stream"); @@ -140,7 +139,7 @@ public class IrisComplex implements DataProvider { -> engine.getDimension().getCaveBiomeStyle().create(rng.nextParallelRNG(InferredType.CAVE.ordinal()), getData()).stream() .zoom(engine.getDimension().getBiomeZoom()) .zoom(r.getCaveBiomeZoom()) - .selectRarity(data.getBiomeLoader().loadAll(r.getCaveBiomes())) + .selectRarity(data.getBiomeLoader().loadAll(r.getCaveBiomes()), legacy) .onNull(emptyBiome) ).convertAware2D(ProceduralStream::get).cache2D("caveBiomeStream", engine, cacheSize).waste("Cave Biome Stream"); inferredStreams.put(InferredType.CAVE, caveBiomeStream); @@ -150,7 +149,7 @@ public class IrisComplex implements DataProvider { .zoom(engine.getDimension().getBiomeZoom()) .zoom(engine.getDimension().getLandZoom()) .zoom(r.getLandBiomeZoom()) - .selectRarity(data.getBiomeLoader().loadAll(r.getLandBiomes(), (t) -> t.setInferredType(InferredType.LAND))) + .selectRarity(data.getBiomeLoader().loadAll(r.getLandBiomes(), (t) -> t.setInferredType(InferredType.LAND)), legacy) ).convertAware2D(ProceduralStream::get) .cache2D("landBiomeStream", engine, cacheSize).waste("Land Biome Stream"); inferredStreams.put(InferredType.LAND, landBiomeStream); @@ -160,7 +159,7 @@ public class IrisComplex implements DataProvider { .zoom(engine.getDimension().getBiomeZoom()) .zoom(engine.getDimension().getSeaZoom()) .zoom(r.getSeaBiomeZoom()) - .selectRarity(data.getBiomeLoader().loadAll(r.getSeaBiomes(), (t) -> t.setInferredType(InferredType.SEA))) + .selectRarity(data.getBiomeLoader().loadAll(r.getSeaBiomes(), (t) -> t.setInferredType(InferredType.SEA)), legacy) ).convertAware2D(ProceduralStream::get) .cache2D("seaBiomeStream", engine, cacheSize).waste("Sea Biome Stream"); inferredStreams.put(InferredType.SEA, seaBiomeStream); @@ -169,7 +168,7 @@ public class IrisComplex implements DataProvider { -> engine.getDimension().getShoreBiomeStyle().create(rng.nextParallelRNG(InferredType.SHORE.ordinal()), getData()).stream() .zoom(engine.getDimension().getBiomeZoom()) .zoom(r.getShoreBiomeZoom()) - .selectRarity(data.getBiomeLoader().loadAll(r.getShoreBiomes(), (t) -> t.setInferredType(InferredType.SHORE))) + .selectRarity(data.getBiomeLoader().loadAll(r.getShoreBiomes(), (t) -> t.setInferredType(InferredType.SHORE)), legacy) ).convertAware2D(ProceduralStream::get).cache2D("shoreBiomeStream", engine, cacheSize).waste("Shore Biome Stream"); inferredStreams.put(InferredType.SHORE, shoreBiomeStream); bridgeStream = focusBiome != null ? ProceduralStream.of((x, z) -> focusBiome.getInferredType(), @@ -302,12 +301,12 @@ public class IrisComplex implements DataProvider { return biome; } - private double interpolateGenerators(Engine engine, IrisInterpolator interpolator, KSet generators, double x, double z, long seed) { + private double interpolateGenerators(Engine engine, IrisInterpolator interpolator, Set generators, double x, double z, long seed) { if (generators.isEmpty()) { return 0; } - KMap cache = new KMap<>(); + HashMap cache = new HashMap<>(64); double hi = interpolator.interpolate(x, z, (xx, zz) -> { try { IrisBiome bx = baseBiomeStream.get(xx, zz); @@ -379,7 +378,7 @@ public class IrisComplex implements DataProvider { } private void registerGenerator(IrisGenerator cachedGenerator) { - generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new KSet<>()).add(cachedGenerator); + generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new HashSet<>()).add(cachedGenerator); } private IrisBiome implode(IrisBiome b, Double x, Double z) { 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 f7ad56a33..7b5d51efd 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -28,12 +28,12 @@ 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.engine.scripting.EngineExecutionEnvironment; import com.volmit.iris.util.atomics.AtomicRollingSequence; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.context.ChunkContext; @@ -43,7 +43,7 @@ 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.MantleFlag; +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; @@ -95,7 +95,7 @@ public class IrisEngine implements Engine { private CompletableFuture hash32; private EngineMode mode; private EngineEffects effects; - private EngineExecutionEnvironment execution; + private EngineEnvironment execution; private EngineWorldManager worldManager; private volatile int parallelism; private boolean failing; @@ -171,10 +171,12 @@ public class IrisEngine implements Engine { cacheId = RNG.r.nextInt(); worldManager = new IrisWorldManager(this); complex = new IrisComplex(this); - execution = new IrisExecutionEnvironment(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() @@ -200,7 +202,7 @@ public class IrisEngine implements Engine { mode.close(); } - mode = getDimension().getMode().getType().create(this); + mode = getDimension().getMode().create(this); } @Override diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java index e58324ad7..b788502e9 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java @@ -29,13 +29,15 @@ import com.volmit.iris.engine.mantle.components.MantleJigsawComponent; import com.volmit.iris.engine.mantle.components.MantleObjectComponent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import lombok.*; import java.io.File; -import java.util.stream.Collectors; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; @Data @EqualsAndHashCode(exclude = "engine") @@ -45,8 +47,9 @@ public class IrisEngineMantle implements EngineMantle { private final Mantle mantle; @Getter(AccessLevel.NONE) private final KMap> components; - private final AtomicCache, Integer>>> componentsCache = new AtomicCache<>(); - private final AtomicCache> disabledFlags = new AtomicCache<>(); + private final KMap registeredComponents = new KMap<>(); + private final AtomicCache, Integer>>> componentsCache = new AtomicCache<>(); + private final AtomicCache> disabledFlags = new AtomicCache<>(); private final MantleObjectComponent object; private final MantleJigsawComponent jigsaw; @@ -75,7 +78,7 @@ public class IrisEngineMantle implements EngineMantle { } @Override - public KList, Integer>> getComponents() { + public List, Integer>> getComponents() { return componentsCache.aquire(() -> { var list = components.keySet() .stream() @@ -86,10 +89,9 @@ public class IrisEngineMantle implements EngineMantle { .mapToInt(MantleComponent::getRadius) .max() .orElse(0); - return new Pair<>(components, radius); + return new Pair<>(List.copyOf(components), radius); }) - .collect(Collectors.toCollection(KList::new)); - + .toList(); int radius = 0; for (var pair : list.reversed()) { @@ -102,19 +104,36 @@ public class IrisEngineMantle implements EngineMantle { } @Override - public void registerComponent(MantleComponent c) { - c.setEnabled(!getDimension().getDisabledComponents().contains(c.getFlag())); + public Map getRegisteredComponents() { + return Collections.unmodifiableMap(registeredComponents); + } + + @Override + public boolean registerComponent(MantleComponent c) { + if (registeredComponents.putIfAbsent(c.getFlag(), c) != null) return false; + c.setEnabled(!getDisabledFlags().contains(c.getFlag())); components.computeIfAbsent(c.getPriority(), k -> new KList<>()).add(c); componentsCache.reset(); + return true; } @Override public KList getComponentFlags() { - return components.values() - .stream() - .flatMap(KList::stream) - .map(MantleComponent::getFlag) - .collect(KList.collector()); + return new KList<>(registeredComponents.keySet()); + } + + @Override + public void hotload() { + disabledFlags.reset(); + for (var component : registeredComponents.values()) { + component.hotload(); + component.setEnabled(!getDisabledFlags().contains(component.getFlag())); + } + componentsCache.reset(); + } + + private Set getDisabledFlags() { + return disabledFlags.aquire(() -> Set.copyOf(getDimension().getDisabledComponents())); } @Override diff --git a/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java b/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java deleted file mode 100644 index 51ca6d1f9..000000000 --- a/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.volmit.iris.Iris; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.object.IrisScript; -import com.volmit.iris.engine.scripting.EngineExecutionEnvironment; -import com.volmit.iris.engine.scripting.IrisScriptingAPI; -import com.volmit.iris.util.format.C; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.apache.bsf.BSFException; -import org.apache.bsf.BSFManager; -import org.apache.bsf.engines.javascript.JavaScriptEngine; - -@Data -@EqualsAndHashCode(exclude = "engine") -@ToString(exclude = "engine") -public class IrisExecutionEnvironment implements EngineExecutionEnvironment { - private final BSFManager manager; - private final Engine engine; - private final IrisScriptingAPI api; - private JavaScriptEngine javaScriptEngine; - - public IrisExecutionEnvironment(Engine engine) { - this.engine = engine; - this.api = new IrisScriptingAPI(engine); - this.manager = new BSFManager(); - this.manager.setClassLoader(Iris.class.getClassLoader()); - try { - this.manager.declareBean("Iris", api, api.getClass()); - this.javaScriptEngine = (JavaScriptEngine) this.manager.loadScriptingEngine("javascript"); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - @Override - public IrisScriptingAPI getAPI() { - return api; - } - - public void execute(String script) { - execute(getEngine().getData().getScriptLoader().load(script)); - } - - public void execute(IrisScript script) { - Iris.debug("Execute Script (void) " + C.DARK_GREEN + script.getLoadKey()); - try { - javaScriptEngine.exec("", 0, 0, script); - } catch (BSFException e) { - e.printStackTrace(); - } - } - - public Object evaluate(String script) { - Iris.debug("Execute Script (for result) " + C.DARK_GREEN + script); - try { - return javaScriptEngine.eval("", 0, 0, getEngine().getData().getScriptLoader().load(script)); - } catch (BSFException e) { - e.printStackTrace(); - } - - return null; - } -} diff --git a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index 70d98045d..e0e974a66 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java @@ -29,7 +29,7 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index af8dac1ed..8c9009119 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -29,13 +29,13 @@ import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.nms.container.BlockPos; import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.pregenerator.ChunkUpdater; +import com.volmit.iris.core.scripting.environment.EngineEnvironment; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.IrisComplex; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.chunk.TerrainChunk; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.object.*; -import com.volmit.iris.engine.scripting.EngineExecutionEnvironment; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.context.ChunkContext; @@ -48,7 +48,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.format.C; import com.volmit.iris.util.function.Function2; import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.BlockPosition; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; @@ -109,7 +109,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat IrisContext getContext(); - EngineExecutionEnvironment getExecution(); + EngineEnvironment getExecution(); double getMaxBiomeObjectDensity(); @@ -295,68 +295,63 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat try { Semaphore semaphore = new Semaphore(3); chunk.raiseFlag(MantleFlag.ETCHED, () -> { - var region = Iris.platform.getRegionScheduler(); - chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> region.run(c.getWorld(), c.getX(), c.getZ(), () -> { - mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> { - Block block = c.getBlock(x, y + getWorld().minHeight(), z); + chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> { + chunk.iterate(TileWrapper.class, (x, y, z, v) -> { + Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15); if (!TileData.setTileState(block, v.getData())) Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey()); }); - }))); - chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> region.run(c.getWorld(), c.getX(), c.getZ(), () -> { - mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> { + }, c, 1)); + chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> { + chunk.iterate(Identifier.class, (x, y, z, v) -> { Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v); }); - }))); + }, c, 1)); - chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> region.runDelayed(c.getWorld(), c.getX(), c.getZ(), () -> { + chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> { PrecisionStopwatch p = PrecisionStopwatch.start(); - KMap updates = new KMap<>(); - RNG r = new RNG(Cache.key(c.getX(), c.getZ())); - mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> { + int[][] grid = new int[16][16]; + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + grid[x][z] = Integer.MIN_VALUE; + } + } + + RNG rng = new RNG(Cache.key(c.getX(), c.getZ())); + chunk.iterate(MatterCavern.class, (x, yf, z, v) -> { int y = yf + getWorld().minHeight(); - if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) { + x &= 15; + z &= 15; + Block block = c.getBlock(x, y, z); + if (!B.isFluid(block.getBlockData())) { return; } - boolean u = false; - if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) { - u = true; - } + boolean u = B.isAir(block.getRelative(BlockFace.DOWN).getBlockData()) + || B.isAir(block.getRelative(BlockFace.WEST).getBlockData()) + || B.isAir(block.getRelative(BlockFace.EAST).getBlockData()) + || B.isAir(block.getRelative(BlockFace.SOUTH).getBlockData()) + || B.isAir(block.getRelative(BlockFace.NORTH).getBlockData()); - if (u) { - updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> { - if (vv != null) { - return Math.max(vv, y); - } - - return y; - }); - } + if (u) grid[x][z] = Math.max(grid[x][z], y); }); - updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r)); - mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + if (grid[x][z] == Integer.MIN_VALUE) + continue; + update(x, grid[x][z], z, c, rng); + } + } + + chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> { int y = yf + getWorld().minHeight(); if (v != null && v.isUpdate()) { - int vx = x & 15; - int vz = z & 15; - update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ()))); - if (vx > 0 && vx < 15 && vz > 0 && vz < 15) { - updateLighting(x, y, z, c); - } + update(x, y, z, c, rng); } }); - mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class); + chunk.deleteSlices(MatterUpdate.class); getMetrics().getUpdates().put(p.getMilliseconds()); - }, RNG.r.i(1, 20)))); + }, c, RNG.r.i(2, 20))); //Why is there a random delay here? }); try { @@ -367,33 +362,21 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat } } - private static Runnable run(Semaphore semaphore, Runnable runnable) { + private static Runnable run(Semaphore semaphore, Runnable runnable, Chunk chunk, int delay) { return () -> { if (!semaphore.tryAcquire()) return; - try { - runnable.run(); - } finally { - semaphore.release(); - } + + Iris.platform.getRegionScheduler().runDelayed(chunk.getWorld(), chunk.getX(), chunk.getZ(), () -> { + try { + runnable.run(); + } finally { + semaphore.release(); + } + }, delay); }; } - @BlockCoordinates - default void updateLighting(int x, int y, int z, Chunk c) { - Block block = c.getBlock(x, y, z); - BlockData data = block.getBlockData(); - - if (B.isLit(data)) { - try { - block.setType(Material.AIR, false); - block.setBlockData(data, true); - } catch (Exception e) { - Iris.reportError(e); - } - } - } - @BlockCoordinates @Override diff --git a/core/src/main/java/com/volmit/iris/engine/framework/PlacedObject.java b/core/src/main/java/com/volmit/iris/engine/framework/PlacedObject.java index e96155572..31cdf1b59 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/PlacedObject.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/PlacedObject.java @@ -22,8 +22,7 @@ import com.volmit.iris.engine.object.IrisObject; import com.volmit.iris.engine.object.IrisObjectPlacement; import lombok.AllArgsConstructor; import lombok.Data; - -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; @Data @AllArgsConstructor diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java b/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java index 1c7fe20b2..0ed617a7d 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java @@ -1,6 +1,6 @@ package com.volmit.iris.engine.mantle; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.ReservedFlag; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -10,5 +10,5 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ComponentFlag { - MantleFlag value(); + ReservedFlag value(); } diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index d37814e0c..16157fb6e 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -26,31 +26,33 @@ import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineTarget; import com.volmit.iris.engine.mantle.components.MantleJigsawComponent; import com.volmit.iris.engine.mantle.components.MantleObjectComponent; -import com.volmit.iris.engine.object.IObjectPlacer; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisPosition; -import com.volmit.iris.engine.object.TileData; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.data.B; -import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.MantleChunk; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; +import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.matter.*; import com.volmit.iris.util.matter.slices.UpdateMatter; -import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.MultiBurst; import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.UnmodifiableView; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; -// TODO: MOVE PLACER OUT OF MATTER INTO ITS OWN THING -public interface EngineMantle extends IObjectPlacer { +import static com.volmit.iris.util.parallel.StreamUtils.forEach; +import static com.volmit.iris.util.parallel.StreamUtils.streamRadius; + +public interface EngineMantle { BlockData AIR = B.get("AIR"); Mantle getMantle(); @@ -61,12 +63,19 @@ public interface EngineMantle extends IObjectPlacer { int getRealRadius(); - KList, Integer>> getComponents(); + @UnmodifiableView + List, Integer>> getComponents(); - void registerComponent(MantleComponent c); + @UnmodifiableView + Map getRegisteredComponents(); + boolean registerComponent(MantleComponent c); + + @UnmodifiableView KList getComponentFlags(); + void hotload(); + default int getHighest(int x, int z) { return getHighest(x, z, getData()); } @@ -87,12 +96,10 @@ public interface EngineMantle extends IObjectPlacer { return getHighest(x, z, getData(), ignoreFluid); } - @Override default int getHighest(int x, int z, IrisData data) { return getHighest(x, z, data, false); } - @Override default int getHighest(int x, int z, IrisData data, boolean ignoreFluid) { return ignoreFluid ? trueHeight(x, z) : Math.max(trueHeight(x, z), getEngine().getDimension().getFluidHeight()); } @@ -101,24 +108,12 @@ public interface EngineMantle extends IObjectPlacer { return getComplex().getRoundedHeighteightStream().get(x, z); } + @Deprecated(forRemoval = true) default boolean isCarved(int x, int h, int z) { return getMantle().get(x, h, z, MatterCavern.class) != null; } - @Override - default void set(int x, int y, int z, BlockData d) { - if (d instanceof IrisCustomData data) { - getMantle().set(x, y, z, data.getBase()); - getMantle().set(x, y, z, data.getCustom()); - } else getMantle().set(x, y, z, d == null ? AIR : d); - } - - @Override - default void setTile(int x, int y, int z, TileData d) { - getMantle().set(x, y, z, new TileWrapper(d)); - } - - @Override + @Deprecated(forRemoval = true) default BlockData get(int x, int y, int z) { BlockData block = getMantle().get(x, y, z, BlockData.class); if (block == null) @@ -126,27 +121,18 @@ public interface EngineMantle extends IObjectPlacer { return block; } - @Override default boolean isPreventingDecay() { return getEngine().getDimension().isPreventLeafDecay(); } - @Override - default boolean isSolid(int x, int y, int z) { - return B.isSolid(get(x, y, z)); - } - - @Override default boolean isUnderwater(int x, int z) { return getHighest(x, z, true) <= getFluidHeight(); } - @Override default int getFluidHeight() { return getEngine().getDimension().getFluidHeight(); } - @Override default boolean isDebugSmartBore() { return getEngine().getDimension().isDebugSmartBore(); } @@ -196,7 +182,7 @@ public interface EngineMantle extends IObjectPlacer { @ChunkCoordinates default void generateMatter(int x, int z, boolean multicore, ChunkContext context) { - if (!getEngine().getDimension().isUseMantle()) { + if (!getEngine().getDimension().isUseMantle() || getMantle().hasFlag(x, z, MantleFlag.PLANNED)) { return; } @@ -206,30 +192,33 @@ public interface EngineMantle extends IObjectPlacer { var pair = iterator.next(); int radius = pair.getB(); boolean last = !iterator.hasNext(); - BurstExecutor burst = burst().burst(radius * 2 + 1); - burst.setMulticore(multicore); + forEach(streamRadius(x, z, radius), + pos -> pair.getA() + .stream() + .filter(MantleComponent::isEnabled) + .map(c -> new Pair<>(c, pos)), + p -> { + MantleComponent c = p.getA(); + Position2 pos = p.getB(); + int xx = pos.getX(); + int zz = pos.getZ(); + IrisContext.getOr(getEngine()).setChunkContext(context); + generateMantleComponent(writer, xx, zz, c, writer.acquireChunk(xx, zz), context); + }, + multicore ? burst() : null + ); - for (int i = -radius; i <= radius; i++) { - for (int j = -radius; j <= radius; j++) { - int xx = x + i; - int zz = z + j; - MantleChunk mc = getMantle().getChunk(xx, zz); - - burst.queue(() -> { - IrisContext.touch(getEngine().getContext()); - pair.getA().forEach(k -> generateMantleComponent(writer, xx, zz, k, mc, context)); - if (last) mc.flag(MantleFlag.PLANNED, true); - }); - } - } - - burst.complete(); + if (!last) continue; + forEach(streamRadius(x, z, radius), + p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true), + multicore ? burst() : null + ); } } } default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) { - mc.raiseFlag(c.getFlag(), () -> { + mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> { if (c.isEnabled()) c.generateLayer(writer, x, z, context); }); } @@ -240,7 +229,12 @@ public interface EngineMantle extends IObjectPlacer { return; } - getMantle().iterateChunk(x, z, t, blocks::set); + var chunk = getMantle().getChunk(x, z).use(); + try { + chunk.iterate(t, blocks::set); + } finally { + chunk.release(); + } } @BlockCoordinates diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java index cfe72ae39..c123183d5 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java @@ -18,7 +18,7 @@ package com.volmit.iris.engine.mantle; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -30,5 +30,32 @@ public abstract class IrisMantleComponent implements MantleComponent { private final EngineMantle engineMantle; private final MantleFlag flag; private final int priority; + + private volatile int radius = -1; + private final Object lock = new Object(); private boolean enabled = true; + + protected abstract int computeRadius(); + + @Override + public void hotload() { + synchronized (lock) { + radius = -1; + } + } + + @Override + public final int getRadius() { + int r = radius; + if(r != -1) return r; + + synchronized (lock) { + if((r = radius) != -1) { + return r; + } + r = computeRadius(); + if(r < 0) r = 0; + return radius = r; + } + } } diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java index 64c69d5c6..b77b095a6 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java @@ -24,7 +24,7 @@ import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.parallel.BurstExecutor; import org.jetbrains.annotations.NotNull; @@ -65,6 +65,8 @@ public interface MantleComponent extends Comparable { void setEnabled(boolean b); + void hotload(); + @ChunkCoordinates void generateLayer(MantleWriter writer, int x, int z, ChunkContext context); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java index ee2c9bf2d..408704925 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java @@ -29,12 +29,16 @@ import com.volmit.iris.engine.object.IrisPosition; import com.volmit.iris.engine.object.TileData; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.IrisCustomData; +import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.function.Function3; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.Matter; +import com.volmit.iris.util.matter.MatterCavern; +import com.volmit.iris.util.matter.TileWrapper; import com.volmit.iris.util.noise.CNG; import lombok.Data; import org.bukkit.block.data.BlockData; @@ -44,6 +48,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static com.volmit.iris.engine.mantle.EngineMantle.AIR; + @Data public class MantleWriter implements IObjectPlacer, AutoCloseable { private final EngineMantle engineMantle; @@ -144,20 +150,41 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { return; } - if (cx >= this.x - radius && cx <= this.x + radius - && cz >= this.z - radius && cz <= this.z + radius) { - MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use()); + MantleChunk chunk = acquireChunk(cx, cz); + if (chunk == null) return; - if (chunk == null) { - Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); - return; - } + Matter matter = chunk.getOrCreate(y >> 4); + matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); + } - Matter matter = chunk.getOrCreate(y >> 4); - matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); - } else { - Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz); + public T getData(int x, int y, int z, Class type) { + int cx = x >> 4; + int cz = z >> 4; + + if (y < 0 || y >= mantle.getWorldHeight()) { + return null; } + + MantleChunk chunk = acquireChunk(cx, cz); + if (chunk == null) { + return null; + } + + return chunk.getOrCreate(y >> 4) + .slice(type) + .get(x & 15, y & 15, z & 15); + } + + @ChunkCoordinates + public MantleChunk acquireChunk(int cx, int cz) { + if (cx < this.x - radius || cx > this.x + radius + || cz < this.z - radius || cz > this.z + radius) { + Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz); + return null; + } + MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use()); + if (chunk == null) Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); + return chunk; } @Override @@ -180,7 +207,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override public BlockData get(int x, int y, int z) { - return getEngineMantle().get(x, y, z); + BlockData block = getData(x, y, z, BlockData.class); + if (block == null) + return AIR; + return block; } @Override @@ -190,12 +220,12 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override public boolean isCarved(int x, int y, int z) { - return getEngineMantle().isCarved(x, y, z); + return getData(x, y, z, MatterCavern.class) != null; } @Override public boolean isSolid(int x, int y, int z) { - return getEngineMantle().isSolid(x, y, z); + return B.isSolid(get(x, y, z)); } @Override @@ -215,7 +245,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override public void setTile(int xx, int yy, int zz, TileData tile) { - getEngineMantle().setTile(xx, yy, zz, tile); + setData(xx, yy, zz, new TileWrapper(tile)); } @Override diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java index eca862c79..dcaf50bee 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java @@ -28,17 +28,13 @@ import com.volmit.iris.engine.object.IrisCarving; import com.volmit.iris.engine.object.IrisRegion; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.documentation.ChunkCoordinates; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.ReservedFlag; import com.volmit.iris.util.math.RNG; -import lombok.Getter; -@Getter -@ComponentFlag(MantleFlag.CARVED) +@ComponentFlag(ReservedFlag.CARVED) public class MantleCarvingComponent extends IrisMantleComponent { - private final int radius = computeRadius(); - public MantleCarvingComponent(EngineMantle engineMantle) { - super(engineMantle, MantleFlag.CARVED, 0); + super(engineMantle, ReservedFlag.CARVED, 0); } @Override @@ -63,7 +59,7 @@ public class MantleCarvingComponent extends IrisMantleComponent { carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0); } - private int computeRadius() { + protected int computeRadius() { var dimension = getDimension(); int max = 0; diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java index 15389715e..ee12b0d6d 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java @@ -28,17 +28,13 @@ import com.volmit.iris.engine.object.IrisFluidBodies; import com.volmit.iris.engine.object.IrisRegion; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.documentation.ChunkCoordinates; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.ReservedFlag; import com.volmit.iris.util.math.RNG; -import lombok.Getter; -@Getter -@ComponentFlag(MantleFlag.FLUID_BODIES) +@ComponentFlag(ReservedFlag.FLUID_BODIES) public class MantleFluidBodyComponent extends IrisMantleComponent { - private final int radius = computeRadius(); - public MantleFluidBodyComponent(EngineMantle engineMantle) { - super(engineMantle, MantleFlag.FLUID_BODIES, 0); + super(engineMantle, ReservedFlag.FLUID_BODIES, 0); } @Override @@ -63,7 +59,7 @@ public class MantleFluidBodyComponent extends IrisMantleComponent { bodies.generate(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4); } - private int computeRadius() { + protected int computeRadius() { int max = 0; max = Math.max(max, getDimension().getFluidBodies().getMaxRange(getData())); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index e5f551872..758b2b662 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -30,7 +30,7 @@ import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.ReservedFlag; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; @@ -40,14 +40,12 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -@ComponentFlag(MantleFlag.JIGSAW) +@ComponentFlag(ReservedFlag.JIGSAW) public class MantleJigsawComponent extends IrisMantleComponent { - @Getter - private final int radius = computeRadius(); private final CNG cng; public MantleJigsawComponent(EngineMantle engineMantle) { - super(engineMantle, MantleFlag.JIGSAW, 1); + super(engineMantle, ReservedFlag.JIGSAW, 2); cng = NoiseStyle.STATIC.create(new RNG(jigsaw())); } @@ -176,10 +174,14 @@ public class MantleJigsawComponent extends IrisMantleComponent { return getEngineMantle().getEngine().getSeedManager().getJigsaw(); } - private int computeRadius() { + protected int computeRadius() { var dimension = getDimension(); KSet structures = new KSet<>(); + if (dimension.getStronghold() != null) { + structures.add(dimension.getStronghold()); + } + for (var placement : dimension.getJigsawStructures()) { structures.add(placement.getStructure()); } diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java index a4ca6dd7a..920bb9b35 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java @@ -33,13 +33,12 @@ import com.volmit.iris.util.data.B; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.ReservedFlag; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.MatterStructurePOI; import com.volmit.iris.util.noise.CNG; import com.volmit.iris.util.noise.NoiseType; import com.volmit.iris.util.parallel.BurstExecutor; -import lombok.Getter; import org.bukkit.util.BlockVector; import java.io.IOException; @@ -47,13 +46,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -@Getter -@ComponentFlag(MantleFlag.OBJECT) +@ComponentFlag(ReservedFlag.OBJECT) public class MantleObjectComponent extends IrisMantleComponent { - private final int radius = computeRadius(); public MantleObjectComponent(EngineMantle engineMantle) { - super(engineMantle, MantleFlag.OBJECT, 1); + super(engineMantle, ReservedFlag.OBJECT, 1); } @Override @@ -157,7 +154,7 @@ public class MantleObjectComponent extends IrisMantleComponent { return v; } - private int computeRadius() { + protected int computeRadius() { var dimension = getDimension(); AtomicInteger xg = new AtomicInteger(); diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java index a7bb391be..678dbcd3b 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java @@ -27,6 +27,7 @@ import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.data.B; +import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.mantle.Mantle; @@ -53,10 +54,11 @@ public class IrisCarveModifier extends EngineAssignedModifier { } @Override + @ChunkCoordinates public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); Mantle mantle = getEngine().getMantle().getMantle(); - MantleChunk mc = getEngine().getMantle().getMantle().getChunk(x, z).use(); + MantleChunk mc = mantle.getChunk(x, z).use(); KMap> positions = new KMap<>(); KMap walls = new KMap<>(); Consumer4 iterator = (xx, yy, zz, c) -> { @@ -81,19 +83,19 @@ public class IrisCarveModifier extends EngineAssignedModifier { //todo: Fix chunk decoration not working on chunk's border - if (rz < 15 && mantle.get(xx, yy, zz + 1, MatterCavern.class) == null) { + if (rz < 15 && mc.get(xx, yy, zz + 1, MatterCavern.class) == null) { walls.put(new IrisPosition(rx, yy, rz + 1), c); } - if (rx < 15 && mantle.get(xx + 1, yy, zz, MatterCavern.class) == null) { + if (rx < 15 && mc.get(xx + 1, yy, zz, MatterCavern.class) == null) { walls.put(new IrisPosition(rx + 1, yy, rz), c); } - if (rz > 0 && mantle.get(xx, yy, zz - 1, MatterCavern.class) == null) { + if (rz > 0 && mc.get(xx, yy, zz - 1, MatterCavern.class) == null) { walls.put(new IrisPosition(rx, yy, rz - 1), c); } - if (rx > 0 && mantle.get(xx - 1, yy, zz, MatterCavern.class) == null) { + if (rx > 0 && mc.get(xx - 1, yy, zz, MatterCavern.class) == null) { walls.put(new IrisPosition(rx - 1, yy, rz), c); } @@ -217,16 +219,21 @@ public class IrisCarveModifier extends EngineAssignedModifier { if (!blocks.hasIndex(i)) { break; } + int y = zone.floor - i - 1; - if (!B.isSolid(output.get(rx, zone.floor - i - 1, rz))) { + BlockData b = blocks.get(i); + BlockData down = output.get(rx, y, rz); + + if (!B.isSolid(down)) { continue; } - if (B.isOre(output.get(rx, zone.floor - i - 1, rz))) { + if (B.isOre(down)) { + output.set(rx, y, rz, B.toDeepSlateOre(down, b)); continue; } - output.set(rx, zone.floor - i - 1, rz, blocks.get(i)); + output.set(rx, y, rz, blocks.get(i)); } blocks = biome.generateCeilingLayers(getDimension(), xx, zz, rng, 3, zone.ceiling, getData(), getComplex()); diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCustomModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCustomModifier.java index 161600101..eca550cd2 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCustomModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCustomModifier.java @@ -5,7 +5,7 @@ import com.volmit.iris.engine.framework.EngineAssignedModifier; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.MultiBurst; import org.bukkit.block.data.BlockData; diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java index d998088b2..dfa7bc1e8 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java @@ -25,9 +25,12 @@ import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.HeightMap; import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.matter.MatterCavern; import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.bukkit.util.BlockVector; @@ -53,28 +56,30 @@ public class IrisDepositModifier extends EngineAssignedModifier { long seed = x * 341873128712L + z * 132897987541L; long mask = 0; + MantleChunk chunk = getEngine().getMantle().getMantle().getChunk(x, z).use(); for (IrisDepositGenerator k : getDimension().getDeposits()) { long finalSeed = seed * ++mask; - burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); + burst.queue(() -> generate(k, chunk, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); } for (IrisDepositGenerator k : region.getDeposits()) { long finalSeed = seed * ++mask; - burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); + burst.queue(() -> generate(k, chunk, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); } for (IrisDepositGenerator k : biome.getDeposits()) { long finalSeed = seed * ++mask; - burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); + burst.queue(() -> generate(k, chunk, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); } burst.complete(); + chunk.release(); } - public void generate(IrisDepositGenerator k, Hunk data, RNG rng, int cx, int cz, boolean safe, ChunkContext context) { - generate(k, data, rng, cx, cz, safe, null, context); + public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk data, RNG rng, int cx, int cz, boolean safe, ChunkContext context) { + generate(k, chunk, data, rng, cx, cz, safe, null, context); } - public void generate(IrisDepositGenerator k, Hunk data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) { + public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) { if (k.getSpawnChance() < rng.d()) return; @@ -122,8 +127,11 @@ public class IrisDepositModifier extends EngineAssignedModifier { if (ny > height || nx > 15 || nx < 0 || ny > getEngine().getHeight() || ny < 0 || nz < 0 || nz > 15) { continue; } + if (!k.isReplaceBedrock() && data.get(nx, ny, nz).getMaterial() == Material.BEDROCK) { + continue; + } - if (!getEngine().getMantle().isCarved((cx << 4) + nx, ny, (cz << 4) + nz)) { + if (chunk.get(nx, ny, nz, MatterCavern.class) == null) { data.set(nx, ny, nz, B.toDeepSlateOre(data.get(nx, ny, nz), clump.getBlocks().get(j))); } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IRare.java b/core/src/main/java/com/volmit/iris/engine/object/IRare.java index 56f4da4d4..f78931830 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IRare.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IRare.java @@ -25,9 +25,9 @@ import com.volmit.iris.util.stream.interpolation.Interpolated; import java.util.List; public interface IRare { - static ProceduralStream stream(ProceduralStream noise, List possibilities) { - return ProceduralStream.of((x, z) -> pick(possibilities, noise.get(x, z)), - (x, y, z) -> pick(possibilities, noise.get(x, y, z)), + static ProceduralStream stream(ProceduralStream noise, List possibilities, boolean legacyRarity) { + return ProceduralStream.of(legacyRarity ? (x, z) -> pickLegacy(possibilities, noise.get(x, z)) : (x, z) -> pick(possibilities, noise.get(x, z)), + legacyRarity ? (x, y, z) -> pickLegacy(possibilities, noise.get(x, y, z)) : (x, y, z) -> pick(possibilities, noise.get(x, y, z)), new Interpolated() { @Override public double toDouble(T t) { @@ -69,6 +69,32 @@ public interface IRare { return null; } + if (possibilities.size() == 1) { + return possibilities.getFirst(); + } + + double total = 0; + for (T i : possibilities) { + total += 1d / i.getRarity(); + } + + double threshold = total * noiseValue; + double buffer = 0; + for (T i : possibilities) { + buffer += 1d / i.getRarity(); + if (buffer >= threshold) { + return i; + } + } + + return possibilities.getLast(); + } + + static T pickLegacy(List possibilities, double noiseValue) { + if (possibilities.isEmpty()) { + return null; + } + if (possibilities.size() == 1) { return possibilities.get(0); } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java index ac1390d83..42caee79e 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java @@ -143,14 +143,14 @@ public class IrisBiome extends IrisRegistrant implements IRare { @Desc("The default wall if iris decides to place a wall higher than 2 blocks (steep hills or possibly cliffs)") private IrisBiomePaletteLayer wall = new IrisBiomePaletteLayer().zero(); @Required - @ArrayType(min = 1, type = IrisBiomePaletteLayer.class) + @ArrayType(type = IrisBiomePaletteLayer.class) @Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.") private KList layers = new KList().qadd(new IrisBiomePaletteLayer()); @Required - @ArrayType(min = 1, type = IrisBiomePaletteLayer.class) + @ArrayType(type = IrisBiomePaletteLayer.class) @Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.") private KList caveCeilingLayers = new KList().qadd(new IrisBiomePaletteLayer()); - @ArrayType(min = 1, type = IrisBiomePaletteLayer.class) + @ArrayType(type = IrisBiomePaletteLayer.class) @Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.") private KList seaLayers = new KList<>(); @ArrayType(min = 1, type = IrisDecorator.class) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java index f3f162a7d..1c9cb514d 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java @@ -61,6 +61,7 @@ public class IrisBlockData extends IrisRegistrant { private int weight = 1; @Desc("If the block cannot be created on this version, Iris will attempt to use this backup block data instead.") private IrisBlockData backup = null; + @RegistryMapBlockState("block") @Desc("Optional properties for this block data such as 'waterlogged': true") private KMap data = new KMap<>(); @Desc("Optional tile data for this block data") diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java index b780d257d..af20daab7 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java @@ -104,7 +104,7 @@ public class IrisCave extends IrisRegistrant { CNG cng = shape.getNoise(base.nextParallelRNG(8131545), engine); KSet mask = shape.getMasked(rng, engine); writer.setNoiseMasked(points, - girth, cng.noise(x, y, z), cng, mask, true, + girth, shape.getNoiseThreshold() < 0 ? cng.noise(x, y, z) : shape.getNoiseThreshold(), cng, mask, true, (xf, yf, zf) -> yf <= h ? w : c); } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java index 4972ce516..3245f2485 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java @@ -1,9 +1,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.object.annotations.Desc; -import com.volmit.iris.engine.object.annotations.RegistryListResource; -import com.volmit.iris.engine.object.annotations.Snippet; +import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.math.M; @@ -25,6 +23,11 @@ public class IrisCaveShape { @Desc("Noise used for the shape of the cave") private IrisGeneratorStyle noise = new IrisGeneratorStyle(); + @MinNumber(0) + @MaxNumber(1) + @Desc("The threshold for noise mask") + private double noiseThreshold = -1; + @RegistryListResource(IrisObject.class) @Desc("Object used as mask for the shape of the cave") private String object = null; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java index d678a4f4d..e7dc2750e 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java @@ -21,6 +21,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; import com.volmit.iris.engine.object.annotations.ArrayType; import com.volmit.iris.engine.object.annotations.Desc; +import com.volmit.iris.engine.object.annotations.Required; import com.volmit.iris.engine.object.annotations.Snippet; import com.volmit.iris.util.collection.KList; import lombok.Data; @@ -37,6 +38,7 @@ import org.bukkit.World; @Data public class IrisCommand { + @Required @ArrayType(min = 1, type = String.class) @Desc("List of commands. Iris replaces {x} {y} and {z} with the location of the entity spawn") private KList commands = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java index e9599d56f..39f36c3b5 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java @@ -33,6 +33,7 @@ import org.bukkit.entity.Player; @Desc("Represents a casting location for a command") @Data public class IrisCommandRegistry { + @Required @ArrayType(min = 1, type = IrisCommand.class) @Desc("Run commands, at the exact location of the player") private KList rawCommands = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java index 19623ca39..b1e1c609b 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java @@ -87,6 +87,8 @@ public class IrisDepositGenerator { @MaxNumber(64) @Desc("Ore varience is how many different objects clumps iris will create") private int varience = 3; + @Desc("If set to true, this deposit will replace bedrock") + private boolean replaceBedrock = false; public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) { KList objects = this.objects.aquire(() -> diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index ef76eac9f..2d7917a6d 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -30,11 +30,12 @@ import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.annotations.functions.ComponentFlagFunction; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.DataProvider; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.noise.CNG; @@ -68,6 +69,7 @@ public class IrisDimension extends IrisRegistrant { private final transient AtomicCache rad = new AtomicCache<>(); private final transient AtomicCache featuresUsed = new AtomicCache<>(); private final transient AtomicCache> strongholdsCache = new AtomicCache<>(); + private final transient AtomicCache>> cachedPreProcessors = new AtomicCache<>(); @MinNumber(2) @Required @Desc("The human readable name of this dimension") @@ -241,9 +243,22 @@ public class IrisDimension extends IrisRegistrant { @Desc("The Subterrain Fluid Layer Height") private int caveLavaHeight = 8; @RegistryListFunction(ComponentFlagFunction.class) - @ArrayType(type = MantleFlag.class) + @ArrayType(type = String.class) @Desc("Collection of disabled components") private KList disabledComponents = new KList<>(); + @Desc("A list of globally applied pre-processors") + @ArrayType(type = IrisPreProcessors.class) + private KList globalPreProcessors = new KList<>(); + @Desc("A list of scripts executed on engine setup") + @RegistryListResource(IrisScript.class) + @ArrayType(type = String.class, min = 1) + private KList engineScripts = new KList<>(); + @Desc("A list of scripts executed on data setup") + @RegistryListResource(IrisScript.class) + @ArrayType(type = String.class, min = 1) + private KList dataScripts = new KList<>(); + @Desc("Use legacy rarity instead of modern one\nWARNING: Changing this may break expressions and image maps") + private boolean legacyRarity = true; public int getMaxHeight() { return (int) getDimensionHeight().getMax(); @@ -364,6 +379,17 @@ public class IrisDimension extends IrisRegistrant { return r; } + public KList getPreProcessors(String type) { + return cachedPreProcessors.aquire(() -> { + KMap> preProcessors = new KMap<>(); + for (var entry : globalPreProcessors) { + preProcessors.computeIfAbsent(entry.getType(), k -> new KList<>()) + .addAll(entry.getScripts()); + } + return preProcessors; + }).get(type); + } + public IrisGeneratorStyle getBiomeStyle(InferredType type) { switch (type) { case CAVE: diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionMode.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionMode.java index ccf3c49b1..07b0e252b 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionMode.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionMode.java @@ -18,7 +18,10 @@ package com.volmit.iris.engine.object; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.EngineMode; import com.volmit.iris.engine.object.annotations.Desc; +import com.volmit.iris.engine.object.annotations.RegistryListResource; import com.volmit.iris.engine.object.annotations.Snippet; import lombok.AllArgsConstructor; import lombok.Data; @@ -35,4 +38,19 @@ public class IrisDimensionMode { @Desc("The dimension type") private IrisDimensionModeType type = IrisDimensionModeType.OVERWORLD; + @RegistryListResource(IrisScript.class) + @Desc("The script to create the dimension mode instead of using provided types") + private String script; + + public EngineMode create(Engine engine) { + if (script == null) { + return type.create(engine); + } + Object result = engine.getExecution().evaluate(script); + if (result instanceof EngineMode) { + return (EngineMode) result; + } + + throw new IllegalStateException("The script '" + script + "' did not return an engine mode!"); + } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java index 7752f6f87..ddec5d9a5 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java @@ -145,7 +145,6 @@ public class IrisEffect { @MinNumber(1) @Desc("The chance is 1 in CHANCE per interval") private int chance = 50; - @ArrayType(min = 1, type = IrisCommandRegistry.class) @Desc("Run commands, with configurable location parameters") private IrisCommandRegistry commandRegistry = null; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java index ba7fa99f6..1ed253a9f 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java @@ -205,9 +205,8 @@ public class IrisEntity extends IrisRegistrant { if (!spawnerScript.isEmpty() && ee == null) { synchronized (this) { - gen.getExecution().getAPI().setLocation(at.clone()); try { - ee = (Entity) gen.getExecution().evaluate(spawnerScript); + ee = (Entity) gen.getExecution().spawnMob(spawnerScript, at.clone()); } catch (Throwable ex) { Iris.error("You must return an Entity in your scripts to use entity scripts!"); ex.printStackTrace(); @@ -332,11 +331,8 @@ public class IrisEntity extends IrisRegistrant { if (postSpawnScripts.isNotEmpty()) { synchronized (this) { - gen.getExecution().getAPI().setLocation(at); - gen.getExecution().getAPI().setEntity(ee); - for (String i : postSpawnScripts) { - gen.getExecution().execute(i); + gen.getExecution().postSpawnMob(i, at, ee); } } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java b/core/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java index 1c1da694b..7f7ae9f47 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisGeneratorStyle.java @@ -18,6 +18,7 @@ package com.volmit.iris.engine.object; +import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; @@ -25,6 +26,7 @@ import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.noise.CNG; import com.volmit.iris.util.noise.ExpressionNoise; import com.volmit.iris.util.noise.ImageNoise; +import com.volmit.iris.util.noise.NoiseGenerator; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -56,6 +58,9 @@ public class IrisGeneratorStyle { private String expression = null; @Desc("Use an Image map instead of a generated value") private IrisImageMap imageMap = null; + @Desc("Instead of using the style property, use a custom noise generator to represent this style.") + @RegistryListResource(IrisScript.class) + private String script = null; @MinNumber(0.00001) @Desc("The Output multiplier. Only used if parent is fracture.") private double multiplier = 1; @@ -93,40 +98,27 @@ public class IrisGeneratorStyle { public CNG createNoCache(RNG rng, IrisData data, boolean actuallyCached) { String cacheKey = hash() + ""; + CNG cng = null; if (getExpression() != null) { IrisExpression e = data.getExpressionLoader().load(getExpression()); - if (e != null) { - CNG cng = new CNG(rng, new ExpressionNoise(rng, e), 1D, 1) - .bake().scale(1D / zoom).pow(exponent).bake(); - cng.setTrueFracturing(axialFracturing); - - if (fracture != null) { - cng.fractureWith(fracture.create(rng.nextParallelRNG(2934), data), fracture.getMultiplier()); - } - - if (cellularFrequency > 0) { - return cng.cellularize(rng.nextParallelRNG(884466), cellularFrequency).scale(1D / cellularZoom).bake(); - } - - return cng; + cng = new CNG(rng, new ExpressionNoise(rng, e), 1D, 1).bake(); } } else if (getImageMap() != null) { - CNG cng = new CNG(rng, new ImageNoise(data, getImageMap()), 1D, 1).bake().scale(1D / zoom).pow(exponent).bake(); - cng.setTrueFracturing(axialFracturing); - - if (fracture != null) { - cng.fractureWith(fracture.create(rng.nextParallelRNG(2934), data), fracture.getMultiplier()); + cng = new CNG(rng, new ImageNoise(data, getImageMap()), 1D, 1).bake(); + } else if (getScript() != null) { + Object result = data.getEnvironment().createNoise(getScript(), rng); + if (result == null) Iris.warn("Failed to create noise from script: " + getScript()); + if (result instanceof NoiseGenerator generator) { + cng = new CNG(rng, generator, 1D, 1).bake(); } - - if (cellularFrequency > 0) { - return cng.cellularize(rng.nextParallelRNG(884466), cellularFrequency).scale(1D / cellularZoom).bake(); - } - - return cng; } - CNG cng = style.create(rng).bake().scale(1D / zoom).pow(exponent).bake(); + if (cng == null) { + cng = style.create(rng).bake(); + } + + cng = cng.scale(1D / zoom).pow(exponent).bake(); cng.setTrueFracturing(axialFracturing); if (fracture != null) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java index c69aca0e4..09d94a6d4 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java @@ -50,8 +50,7 @@ public class IrisJigsawPiece extends IrisRegistrant { @Desc("The object this piece represents") private String object = ""; - @Required - @ArrayType(type = IrisJigsawPieceConnector.class, min = 1) + @ArrayType(type = IrisJigsawPieceConnector.class) @Desc("The connectors this object contains") private KList connectors = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index 0338ab28d..210278bda 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -514,9 +514,9 @@ public class IrisObject extends IrisRegistrant { max.setZ(Math.max(max.getZ(), i.getZ())); } - w = max.getBlockX() - min.getBlockX() + (min.getBlockX() <= 0 && max.getBlockX() >= 0 && min.getBlockX() != max.getBlockX() ? 1 : 0); - h = max.getBlockY() - min.getBlockY() + (min.getBlockY() <= 0 && max.getBlockY() >= 0 && min.getBlockY() != max.getBlockY() ? 1 : 0); - d = max.getBlockZ() - min.getBlockZ() + (min.getBlockZ() <= 0 && max.getBlockZ() >= 0 && min.getBlockZ() != max.getBlockZ() ? 1 : 0); + w = max.getBlockX() - min.getBlockX() + 1; + h = max.getBlockY() - min.getBlockY() + 1; + d = max.getBlockZ() - min.getBlockZ() + 1; center = new BlockVector(w / 2, h / 2, d / 2); } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisPreProcessors.java b/core/src/main/java/com/volmit/iris/engine/object/IrisPreProcessors.java new file mode 100644 index 000000000..fb62a9d78 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisPreProcessors.java @@ -0,0 +1,25 @@ +package com.volmit.iris.engine.object; + +import com.volmit.iris.engine.object.annotations.*; +import com.volmit.iris.engine.object.annotations.functions.ResourceLoadersFunction; +import com.volmit.iris.util.collection.KList; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Desc("Represents global preprocessors") +public class IrisPreProcessors { + @Required + @Desc("The preprocessor type") + @RegistryListFunction(ResourceLoadersFunction.class) + private String type = "dimension"; + + @Required + @Desc("The preprocessor scripts") + @RegistryListResource(IrisScript.class) + @ArrayType(type = String.class, min = 1) + private KList scripts = new KList<>(); +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryMapBlockState.java b/core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryMapBlockState.java new file mode 100644 index 000000000..4bcf14cef --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryMapBlockState.java @@ -0,0 +1,12 @@ +package com.volmit.iris.engine.object.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface RegistryMapBlockState { + String value(); +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java b/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java index 7a0c01755..fb8738ffe 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java +++ b/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java @@ -6,7 +6,7 @@ import com.volmit.iris.engine.framework.ListFunction; import com.volmit.iris.engine.mantle.ComponentFlag; import com.volmit.iris.engine.mantle.MantleComponent; import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.mantle.flag.MantleFlag; import java.util.Objects; diff --git a/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ResourceLoadersFunction.java b/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ResourceLoadersFunction.java new file mode 100644 index 000000000..943a96113 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ResourceLoadersFunction.java @@ -0,0 +1,28 @@ +package com.volmit.iris.engine.object.annotations.functions; + +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.loader.ResourceLoader; +import com.volmit.iris.engine.framework.ListFunction; +import com.volmit.iris.util.collection.KList; + +public class ResourceLoadersFunction implements ListFunction> { + @Override + public String key() { + return "resource-loader"; + } + + @Override + public String fancyName() { + return "Resource Loader"; + } + + @Override + public KList apply(IrisData data) { + return data.getLoaders() + .values() + .stream() + .filter(rl -> ResourceLoader.class.equals(rl.getClass())) + .map(ResourceLoader::getFolderName) + .collect(KList.collector()); + } +} 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 9ad0eba8a..060e80bb4 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 @@ -19,6 +19,7 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisWorlds; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; @@ -34,7 +35,6 @@ import com.volmit.iris.engine.object.StudioMode; import com.volmit.iris.engine.platform.studio.StudioGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.data.IrisBiomeStorage; -import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder; import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder; import com.volmit.iris.util.io.ReactiveFolder; @@ -45,8 +45,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Setter; import org.bukkit.*; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -205,7 +203,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun IrisBiomeStorage st = new IrisBiomeStorage(); TerrainChunk tc = TerrainChunk.createUnsafe(world, st); this.world.bind(world); - getEngine().generate(x << 4, z << 4, tc, false); + getEngine().generate(x << 4, z << 4, tc, IrisSettings.get().getGenerator().useMulticore); Chunk c = Iris.platform.getChunkAtAsync(world, x, z) .thenApply(d -> { @@ -367,7 +365,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } else { ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight()); - getEngine().generate(x << 4, z << 4, blocks, biomes, false); + getEngine().generate(x << 4, z << 4, blocks, biomes, IrisSettings.get().getGenerator().useMulticore); blocks.apply(); biomes.apply(); } diff --git a/core/src/main/java/com/volmit/iris/engine/scripting/EngineExecutionEnvironment.java b/core/src/main/java/com/volmit/iris/engine/scripting/EngineExecutionEnvironment.java deleted file mode 100644 index 464feb77a..000000000 --- a/core/src/main/java/com/volmit/iris/engine/scripting/EngineExecutionEnvironment.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.scripting; - -import com.volmit.iris.engine.framework.Engine; -import org.apache.bsf.BSFManager; - -public interface EngineExecutionEnvironment { - Engine getEngine(); - - IrisScriptingAPI getAPI(); - - BSFManager getManager(); - - void execute(String script); - - Object evaluate(String script); - - default void close() { - - } -} diff --git a/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java b/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java deleted file mode 100644 index ae613c48f..000000000 --- a/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.scripting; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.loader.IrisRegistrant; -import com.volmit.iris.engine.IrisComplex; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.object.IrisBiome; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.engine.object.IrisExpression; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.bukkit.Location; -import org.bukkit.entity.Entity; - -@Data -@EqualsAndHashCode(exclude = "engine") -@ToString(exclude = "engine") -public class IrisScriptingAPI { - private final Engine engine; - private IrisRegistrant preprocessorObject; - private double x = 0; - private double y = 0; - private double z = 0; - private Location location; - private Entity entity; - - public IrisScriptingAPI(Engine engine) { - this.engine = engine; - } - - public IrisData getData() { - return getEngine().getData(); - } - - public IrisComplex getComplex() { - return getEngine().getComplex(); - } - - public long getSeed() { - return getEngine().getSeedManager().getScript(); - } - - public double expression(String expressionName, double x, double y, double z) { - IrisExpression expression = getData().getExpressionLoader().load(expressionName); - return expression.evaluate(getComplex().getRng(), x, y, z); - } - - public double expression(String expressionName, double x, double z) { - IrisExpression expression = getData().getExpressionLoader().load(expressionName); - return expression.evaluate(getComplex().getRng(), x, z); - } - - public IrisBiome getBiomeAt(int x, int z) { - return getEngine().getSurfaceBiome(x, z); - } - - public IrisDimension getDimension() { - return getEngine().getDimension(); - } - - public void info(String log) { - Iris.info(log); - } - - public void debug(String log) { - Iris.debug(log); - } - - public void warn(String log) { - Iris.warn(log); - } - - public void error(String log) { - Iris.error(log); - } -} diff --git a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java index 73bae0e5e..a6b72cf9f 100644 --- a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java +++ b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java @@ -29,7 +29,7 @@ import lombok.Data; @Data public class IrisContext { private static final KMap context = new KMap<>(); - private static ChronoLatch cl = new ChronoLatch(60000); + private static final ChronoLatch cl = new ChronoLatch(60000); private final Engine engine; private ChunkContext chunkContext; @@ -53,9 +53,10 @@ public class IrisContext { } public static void touch(IrisContext c) { - synchronized (context) { - context.put(Thread.currentThread(), c); + context.put(Thread.currentThread(), c); + if (!cl.couldFlip()) return; + synchronized (cl) { if (cl.flip()) { dereference(); } @@ -63,15 +64,13 @@ public class IrisContext { } 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); + 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.threadId()); } + + context.remove(i); } } } diff --git a/core/src/main/java/com/volmit/iris/util/data/B.java b/core/src/main/java/com/volmit/iris/util/data/B.java index 7e71d39f3..7f98d13f9 100644 --- a/core/src/main/java/com/volmit/iris/util/data/B.java +++ b/core/src/main/java/com/volmit/iris/util/data/B.java @@ -22,6 +22,8 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.link.data.DataType; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.nms.container.BlockProperty; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -35,10 +37,7 @@ import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.type.Leaves; import org.bukkit.block.data.type.PointedDripstone; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static org.bukkit.Material.*; @@ -610,8 +609,7 @@ public class B { } public static boolean isUpdatable(BlockData mat) { - return isLit(mat) - || isStorage(mat) + return isStorage(mat) || (mat instanceof PointedDripstone && ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP)); } @@ -681,6 +679,28 @@ public class B { return bt.toArray(new String[0]); } + public synchronized static KMap, List> getBlockStates() { + KMap, List> flipped = new KMap<>(); + INMS.get().getBlockProperties().forEach((k, v) -> { + flipped.computeIfAbsent(v, $ -> new KList<>()).add(k.getKey().toString()); + }); + + var emptyStates = flipped.computeIfAbsent(new KList<>(0), $ -> new KList<>()); + for (var pair : Iris.service(ExternalDataSVC.class).getAllBlockProperties()) { + if (pair.getB().isEmpty()) emptyStates.add(pair.getA().toString()); + else flipped.computeIfAbsent(pair.getB(), $ -> new KList<>()).add(pair.getA().toString()); + } + emptyStates.addAll(custom.k()); + + KMap, List> states = new KMap<>(); + flipped.forEach((k, v) -> { + var old = states.put(v, k); + if (old != null) Iris.error("Duplicate block state: " + v + " (" + old + " and " + k + ")"); + }); + + return states; + } + public static String[] getItemTypes() { KList bt = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/util/data/KCache.java b/core/src/main/java/com/volmit/iris/util/data/KCache.java index 5f33cdaeb..0226dedb7 100644 --- a/core/src/main/java/com/volmit/iris/util/data/KCache.java +++ b/core/src/main/java/com/volmit/iris/util/data/KCache.java @@ -21,10 +21,16 @@ package com.volmit.iris.util.data; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import com.github.benmanes.caffeine.cache.Scheduler; import com.volmit.iris.engine.framework.MeteredCache; import com.volmit.iris.util.math.RollingSequence; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + public class KCache implements MeteredCache { + public static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor(); + private final long max; private final LoadingCache cache; private final boolean fastDump; @@ -46,6 +52,8 @@ public class KCache implements MeteredCache { return Caffeine .newBuilder() .maximumSize(max) + .scheduler(Scheduler.systemScheduler()) + .executor(EXECUTOR) .initialCapacity((int) (max)) .build((k) -> loader == null ? null : loader.load(k)); } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java index 2afc0c3b4..4ed18fa2d 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java @@ -19,12 +19,12 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.data.Varint; +import lombok.Getter; import org.apache.commons.lang3.Validate; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLongArray; import java.util.function.IntConsumer; @@ -52,8 +52,10 @@ public class DataBits { 0, 5}; private final AtomicLongArray data; + @Getter private final int bits; private final long mask; + @Getter private final int size; private final int valuesPerLong; private final int divideMul; @@ -149,18 +151,9 @@ public class DataBits { return data; } - public int getSize() { - return size; - } - - public int getBits() { - return bits; - } - public DataBits setBits(int newBits) { if (bits != newBits) { DataBits newData = new DataBits(newBits, size); - AtomicInteger c = new AtomicInteger(0); for (int i = 0; i < size; i++) { newData.set(i, get(i)); diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java index 44d25a1e7..ab672ca76 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java @@ -19,78 +19,32 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.data.Varint; +import lombok.Synchronized; import java.io.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; public class DataContainer { protected static final int INITIAL_BITS = 3; protected static final int LINEAR_BITS_LIMIT = 4; protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1; protected static final int[] BIT = computeBitLimits(); - private final AtomicReference> palette; - private final AtomicReference data; - private final AtomicInteger bits; + private volatile Palette palette; + private volatile DataBits data; private final int length; private final Writable writer; public DataContainer(Writable writer, int length) { this.writer = writer; this.length = length; - this.bits = new AtomicInteger(INITIAL_BITS); - this.data = new AtomicReference<>(new DataBits(INITIAL_BITS, length)); - this.palette = new AtomicReference<>(newPalette(INITIAL_BITS)); + this.data = new DataBits(INITIAL_BITS, length); + this.palette = newPalette(INITIAL_BITS); } public DataContainer(DataInputStream din, Writable writer) throws IOException { this.writer = writer; this.length = Varint.readUnsignedVarInt(din); - this.palette = new AtomicReference<>(newPalette(din)); - this.data = new AtomicReference<>(new DataBits(palette.get().bits(), length, din)); - this.bits = new AtomicInteger(palette.get().bits()); - } - - public static String readBitString(DataInputStream din) throws IOException { - DataContainer c = new DataContainer<>(din, new Writable() { - @Override - public Character readNodeData(DataInputStream din) throws IOException { - return din.readChar(); - } - - @Override - public void writeNodeData(DataOutputStream dos, Character character) throws IOException { - dos.writeChar(character); - } - }); - - StringBuilder sb = new StringBuilder(); - - for (int i = c.size() - 1; i >= 0; i--) { - sb.setCharAt(i, c.get(i)); - } - - return sb.toString(); - } - - public static void writeBitString(String s, DataOutputStream dos) throws IOException { - DataContainer c = new DataContainer<>(new Writable() { - @Override - public Character readNodeData(DataInputStream din) throws IOException { - return din.readChar(); - } - - @Override - public void writeNodeData(DataOutputStream dos, Character character) throws IOException { - dos.writeChar(character); - } - }, s.length()); - - for (int i = 0; i < s.length(); i++) { - c.set(i, s.charAt(i)); - } - - c.writeDos(dos); + this.palette = newPalette(din); + this.data = new DataBits(palette.bits(), length, din); } private static int[] computeBitLimits() { @@ -117,17 +71,9 @@ public class DataContainer { return DataContainer.BIT.length - 1; } - public DataBits getData() { - return data.get(); - } - - public Palette getPalette() { - return palette.get(); - } - public String toString() { - return "DataContainer <" + length + " x " + bits + " bits> -> Palette<" + palette.get().getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.get().size() + - " " + data.get().toString() + " PalBit: " + palette.get().bits(); + return "DataContainer <" + length + " x " + data.getBits() + " bits> -> Palette<" + palette.getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.size() + + " " + data.toString() + " PalBit: " + palette.bits(); } public byte[] write() throws IOException { @@ -140,11 +86,12 @@ public class DataContainer { writeDos(new DataOutputStream(out)); } + @Synchronized public void writeDos(DataOutputStream dos) throws IOException { Varint.writeUnsignedVarInt(length, dos); - Varint.writeUnsignedVarInt(palette.get().size(), dos); - palette.get().iterateIO((data, __) -> writer.writeNodeData(dos, data)); - data.get().write(dos); + Varint.writeUnsignedVarInt(palette.size(), dos); + palette.iterateIO((data, __) -> writer.writeNodeData(dos, data)); + data.write(dos); dos.flush(); } @@ -163,55 +110,44 @@ public class DataContainer { return new HashPalette<>(); } - public void ensurePaletted(T t) { - if (palette.get().id(t) == -1) { - expandOne(); - } - } - + @Synchronized public void set(int position, T t) { - synchronized (this) { - int id = palette.get().id(t); + int id = palette.id(t); - if (id == -1) { - expandOne(); - id = palette.get().add(t); - } - - data.get().set(position, id); + if (id == -1) { + id = palette.add(t); + updateBits(); } + + data.set(position, id); } - private void expandOne() { - if (palette.get().size() + 1 >= BIT[bits.get()]) { - setBits(bits.get() + 1); + @Synchronized + @SuppressWarnings("NonAtomicOperationOnVolatileField") + private void updateBits() { + if (palette.bits() == data.getBits()) + return; + + int bits = palette.bits(); + if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) { + palette = newPalette(bits).from(palette); } + + data = data.setBits(bits); } + @Synchronized public T get(int position) { - synchronized (this) { - int id = data.get().get(position) + 1; + int id = data.get(position); - if (id <= 0) { - return null; - } - - return palette.get().get(id - 1); + if (id <= 0) { + return null; } - } - public void setBits(int bits) { - if (this.bits.get() != bits) { - if (this.bits.get() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) { - palette.set(newPalette(bits).from(palette.get())); - } - - this.bits.set(bits); - data.set(data.get().setBits(bits)); - } + return palette.get(id); } public int size() { - return getData().getSize(); + return data.getSize(); } } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java index c35c02acc..134425f6f 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java @@ -21,26 +21,25 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.function.Consumer2; +import java.io.DataInputStream; +import java.io.IOException; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; public class HashPalette implements Palette { - private final ReentrantLock lock = new ReentrantLock(); private final LinkedHashMap palette; private final KMap lookup; private final AtomicInteger size; public HashPalette() { - this.size = new AtomicInteger(0); + this.size = new AtomicInteger(1); this.palette = new LinkedHashMap<>(); this.lookup = new KMap<>(); - add(null); } @Override public T get(int id) { - if (id < 0 || id >= size.get()) { + if (id <= 0 || id >= size.get()) { return null; } @@ -49,17 +48,16 @@ public class HashPalette implements Palette { @Override public int add(T t) { - lock.lock(); - try { - int index = size.getAndIncrement(); - palette.put(t, index); + if (t == null) { + return 0; + } - if (t != null) { + synchronized (palette) { + return palette.computeIfAbsent(t, $ -> { + int index = size.getAndIncrement(); lookup.put(index, t); - } - return index; - } finally { - lock.unlock(); + return index; + }); } } @@ -80,17 +78,33 @@ public class HashPalette implements Palette { @Override public void iterate(Consumer2 c) { - lock.lock(); - try { - for (T i : palette.keySet()) { - if (i == null) { - continue; - } - - c.accept(i, id(i)); + synchronized (palette) { + for (int i = 1; i < size.get(); i++) { + c.accept(lookup.get(i), i); } - } finally { - lock.unlock(); } } + + @Override + public Palette from(Palette oldPalette) { + oldPalette.iterate((t, i) -> { + if (t == null) throw new NullPointerException("Null palette entries are not allowed!"); + lookup.put(i, t); + palette.put(t, i); + }); + size.set(oldPalette.size() + 1); + return this; + } + + @Override + public Palette from(int size, Writable writable, DataInputStream in) throws IOException { + for (int i = 1; i <= size; i++) { + T t = writable.readNodeData(in); + if (t == null) throw new NullPointerException("Null palette entries are not allowed!"); + lookup.put(i, t); + palette.put(t, i); + } + this.size.set(size + 1); + return this; + } } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java index f83183384..5e2c1d43e 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java @@ -19,19 +19,19 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.function.Consumer2; +import lombok.Synchronized; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceArray; public class LinearPalette implements Palette { - private final AtomicReference> palette; + private volatile AtomicReferenceArray palette; private final AtomicInteger size; public LinearPalette(int initialSize) { - this.size = new AtomicInteger(0); - this.palette = new AtomicReference<>(new AtomicReferenceArray<>(initialSize)); - palette.get().set(size.getAndIncrement(), null); + this.size = new AtomicInteger(1); + this.palette = new AtomicReferenceArray<>(initialSize); + palette.set(0, null); } @Override @@ -40,26 +40,29 @@ public class LinearPalette implements Palette { return null; } - return palette.get().get(id); + return palette.get(id); } @Override public int add(T t) { + if (t == null) { + return 0; + } int index = size.getAndIncrement(); grow(index + 1); - palette.get().set(index, t); + palette.set(index, t); return index; } - private void grow(int newLength) { - if (newLength > palette.get().length()) { - AtomicReferenceArray a = new AtomicReferenceArray<>(newLength + size.get()); + private synchronized void grow(int newLength) { + if (newLength > palette.length()) { + AtomicReferenceArray a = new AtomicReferenceArray<>(newLength); - for (int i = 0; i < palette.get().length(); i++) { - a.set(i, palette.get().get(i)); + for (int i = 0; i < palette.length(); i++) { + a.set(i, palette.get(i)); } - palette.set(a); + palette = a; } } @@ -69,8 +72,8 @@ public class LinearPalette implements Palette { return 0; } - for (int i = 1; i < size() + 1; i++) { - if (t.equals(palette.get().get(i))) { + for (int i = 1; i < size.get(); i++) { + if (t.equals(palette.get(i))) { return i; } } @@ -85,8 +88,8 @@ public class LinearPalette implements Palette { @Override public void iterate(Consumer2 c) { - for (int i = 1; i < size() + 1; i++) { - c.accept(palette.get().get(i), i); + for (int i = 1; i <= size(); i++) { + c.accept(palette.get(i), i); } } } diff --git a/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java b/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java index 6239518b1..65fe8a36d 100644 --- a/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java +++ b/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java @@ -44,14 +44,14 @@ public class CountingDataInputStream extends DataInputStream { } @Override - public int read(@NotNull byte[] b, int off, int len) throws IOException { + public int read(byte @NotNull [] b, int off, int len) throws IOException { int i = in.read(b, off, len); - count(i); + if (i != -1) count(i); return i; } private void count(int i) { - count += i; + count = Math.addExact(count, i); if (mark == -1) return; @@ -69,6 +69,12 @@ public class CountingDataInputStream extends DataInputStream { public synchronized void mark(int readlimit) { if (!in.markSupported()) return; in.mark(readlimit); + if (readlimit <= 0) { + mark = -1; + markLimit = 0; + return; + } + mark = count; markLimit = readlimit; } diff --git a/core/src/main/java/com/volmit/iris/util/io/IO.java b/core/src/main/java/com/volmit/iris/util/io/IO.java index 247dfefb1..7506c5651 100644 --- a/core/src/main/java/com/volmit/iris/util/io/IO.java +++ b/core/src/main/java/com/volmit/iris/util/io/IO.java @@ -24,14 +24,26 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.volmit.iris.Iris; import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.scheduling.J; import org.apache.commons.io.function.IOConsumer; import org.apache.commons.io.function.IOFunction; +import lombok.SneakyThrows; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.SAXReader; +import org.dom4j.io.XMLWriter; import java.io.*; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -1663,18 +1675,47 @@ public class IO { return (ch2 == -1); } + @SneakyThrows + public static void write(File file, Document doc) { + file.getParentFile().mkdirs(); + try (var writer = new FileWriter(file)) { + new XMLWriter(writer, OutputFormat.createPrettyPrint()) + .write(doc); + } + } + + @SneakyThrows + public static Document read(File file) { + if (file.exists()) return new SAXReader().read(file); + var doc = DocumentHelper.createDocument(); + doc.addElement("project") + .addAttribute("version", "4"); + return doc; + } + public static void write(File file, IOFunction builder, IOConsumer action) throws IOException { File dir = new File(file.getParentFile(), ".tmp"); dir.mkdirs(); dir.deleteOnExit(); File temp = File.createTempFile("iris",".bin", dir); - try { + try (var target = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.SYNC)) { + lock(target); + try (var out = builder.apply(new FileOutputStream(temp))) { action.accept(out); } - Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(temp.toPath(), Channels.newOutputStream(target)); } finally { temp.delete(); } } + + public static FileLock lock(FileChannel channel) throws IOException { + while (true) { + try { + return channel.lock(); + } catch (OverlappingFileLockException e) {} + J.sleep(1); + } + } } diff --git a/core/src/main/java/com/volmit/iris/util/io/JarScanner.java b/core/src/main/java/com/volmit/iris/util/io/JarScanner.java index d7dcd31e7..cf8aa3271 100644 --- a/core/src/main/java/com/volmit/iris/util/io/JarScanner.java +++ b/core/src/main/java/com/volmit/iris/util/io/JarScanner.java @@ -83,6 +83,28 @@ public class JarScanner { zip.close(); } + public void scanAll() throws IOException { + classes.clear(); + FileInputStream fin = new FileInputStream(jar); + ZipInputStream zip = new ZipInputStream(fin); + for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { + if (!entry.isDirectory() && entry.getName().endsWith(".class")) { + String c = entry.getName().replaceAll("/", ".").replace(".class", ""); + + if (c.startsWith(superPackage)) { + try { + Class clazz = Class.forName(c); + classes.add(clazz); + } catch (Throwable e) { + if (!report) continue; + Iris.reportError(e); + e.printStackTrace(); + } + } + } + } + } + /** * Get the scanned clases * diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index c48c43bd8..3867a2fc8 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -35,6 +35,8 @@ import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.io.IO; +import com.volmit.iris.util.mantle.io.IOWorker; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.M; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.MatterSlice; @@ -44,9 +46,7 @@ import com.volmit.iris.util.parallel.MultiBurst; import lombok.Getter; import org.bukkit.Chunk; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -69,6 +69,7 @@ public class Mantle { private final MultiBurst ioBurst; private final Semaphore ioTrim; private final Semaphore ioTectonicUnload; + private final IOWorker worker; private final AtomicDouble adjustedIdleDuration; private final KSet toUnload; @@ -88,9 +89,10 @@ public class Mantle { this.ioTectonicUnload = new Semaphore(LOCK_SIZE, true); loadedRegions = new KMap<>(); lastUse = new KMap<>(); - ioBurst = MultiBurst.burst; + ioBurst = MultiBurst.ioBurst; adjustedIdleDuration = new AtomicDouble(0); toUnload = new KSet<>(); + worker = new IOWorker(dataFolder, worldHeight); Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); } @@ -379,7 +381,7 @@ public class Mantle { loadedRegions.forEach((i, plate) -> b.queue(() -> { try { plate.close(); - plate.write(fileForRegion(dataFolder, i, false)); + worker.write(fileForRegion(dataFolder, i, false).getName(), plate); oldFileForRegion(dataFolder, i).delete(); } catch (Throwable e) { Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i)); @@ -394,6 +396,11 @@ public class Mantle { } catch (Throwable e) { Iris.reportError(e); } + try { + worker.close(); + } catch (Throwable e) { + Iris.reportError(e); + } IO.delete(new File(dataFolder, ".tmp")); Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); @@ -462,8 +469,8 @@ public class Mantle { ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE); try { + double unloadTime = M.ms() - adjustedIdleDuration.get(); for (long id : toUnload) { - double unloadTime = M.ms() - adjustedIdleDuration.get(); burst.queue(() -> hyperLock.withLong(id, () -> { TectonicPlate m = loadedRegions.get(id); if (m == null) { @@ -484,14 +491,15 @@ public class Mantle { } try { - m.write(fileForRegion(dataFolder, id, false)); + m.close(); + worker.write(fileForRegion(dataFolder, id, false).getName(), m); oldFileForRegion(dataFolder, id).delete(); loadedRegions.remove(id, m); lastUse.remove(id); toUnload.remove(id); i.incrementAndGet(); Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); - } catch (IOException e) { + } catch (IOException | InterruptedException e) { Iris.reportError(e); } })); @@ -524,19 +532,17 @@ public class Mantle { if (!trim || !unload) { try { return getSafe(x, z).get(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { + } catch (Throwable e) { e.printStackTrace(); } - } + } else { + Long key = key(x, z); + TectonicPlate p = loadedRegions.get(key); - Long key = key(x, z); - TectonicPlate p = loadedRegions.get(key); - - if (p != null && !p.isClosed()) { - use(key); - return p; + if (p != null && !p.isClosed()) { + use(key); + return p; + } } try { @@ -547,6 +553,9 @@ public class Mantle { } catch (ExecutionException e) { Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)"); Iris.reportError(e); + } catch (Throwable e) { + Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception"); + Iris.reportError(e); } } finally { if (trim) ioTrim.release(); @@ -570,17 +579,17 @@ public class Mantle { return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> { Long k = key(x, z); use(k); - TectonicPlate region = loadedRegions.get(k); - - if (region != null && !region.isClosed()) { - return region; + TectonicPlate r = loadedRegions.get(k); + if (r != null && !r.isClosed()) { + return r; } + TectonicPlate region; File file = fileForRegion(dataFolder, x, z); if (file.exists()) { try { Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); - region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv.")); + region = worker.read(file.getName()); if (region.getX() != x || region.getZ() != z) { Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z); diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java index 1c50d8c98..01153403c 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java @@ -21,20 +21,23 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates; import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.io.CountingDataInputStream; +import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.matter.IrisMatter; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.MatterSlice; +import com.volmit.iris.util.parallel.AtomicBooleanArray; import lombok.Getter; import lombok.SneakyThrows; +import org.jetbrains.annotations.Nullable; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicReferenceArray; /** @@ -46,7 +49,8 @@ public class MantleChunk { private final int x; @Getter private final int z; - private final AtomicIntegerArray flags; + private final AtomicBooleanArray flags; + private final Object[] flagLocks; private final AtomicReferenceArray sections; private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true); private final AtomicBoolean closed = new AtomicBoolean(false); @@ -59,12 +63,13 @@ public class MantleChunk { @ChunkCoordinates public MantleChunk(int sectionHeight, int x, int z) { sections = new AtomicReferenceArray<>(sectionHeight); - flags = new AtomicIntegerArray(MantleFlag.values().length); + flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1); + flagLocks = new Object[flags.length()]; this.x = x; this.z = z; for (int i = 0; i < flags.length(); i++) { - flags.set(i, 0); + flagLocks[i] = new Object(); } } @@ -79,10 +84,19 @@ public class MantleChunk { public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { this(sectionHeight, din.readByte(), din.readByte()); int s = din.readByte(); - int l = version < 0 ? flags.length() : Varint.readUnsignedVarInt(din); + int l = version < 0 ? MantleFlag.RESERVED_FLAGS : Varint.readUnsignedVarInt(din); - for (int i = 0; i < flags.length() && i < l; i++) { - flags.set(i, din.readBoolean() ? 1 : 0); + if (version >= 1) { + for (int i = 0; i < l;) { + byte f = din.readByte(); + for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) { + flags.set(i, (f & (1 << j)) != 0); + } + } + } else { + for (int i = 0; i < flags.length() && i < l; i++) { + flags.set(i, din.readBoolean()); + } } for (int i = 0; i < s; i++) { @@ -109,6 +123,9 @@ public class MantleChunk { din.skipTo(end); TectonicPlate.addError(); } + if (din.count() != start + size) { + throw new IOException("Chunk section read size mismatch!"); + } } } @@ -137,21 +154,35 @@ public class MantleChunk { ref.release(); } + public void copyFlags(MantleChunk chunk) { + use(); + for (int i = 0; i < flags.length(); i++) { + flags.set(i, chunk.flags.get(i)); + } + release(); + } + public void flag(MantleFlag flag, boolean f) { if (closed.get()) throw new IllegalStateException("Chunk is closed!"); - flags.set(flag.ordinal(), f ? 1 : 0); + flags.set(flag.ordinal(), f); } public void raiseFlag(MantleFlag flag, Runnable r) { - synchronized (this) { - if (!isFlagged(flag)) flag(flag, true); - else return; + raiseFlag(null, flag, r); + } + + public void raiseFlag(@Nullable MantleFlag guard, MantleFlag flag, Runnable r) { + if (closed.get()) throw new IllegalStateException("Chunk is closed!"); + if (guard != null && isFlagged(guard)) return; + synchronized (flagLocks[flag.ordinal()]) { + if (flags.compareAndSet(flag.ordinal(), false, true)) { + r.run(); + } } - r.run(); } public boolean isFlagged(MantleFlag flag) { - return flags.get(flag.ordinal()) == 1; + return flags.get(flag.ordinal()); } /** @@ -176,6 +207,15 @@ public class MantleChunk { return sections.get(section); } + @Nullable + @ChunkRelativeBlockCoordinates + @SuppressWarnings("unchecked") + public T get(int x, int y, int z, Class type) { + return (T) getOrCreate(y >> 4) + .slice(type) + .get(x & 15, y & 15, z & 15); + } + /** * Clear all matter from this chunk */ @@ -207,7 +247,9 @@ public class MantleChunk { if (matter == null) { matter = new IrisMatter(16, 16, 16); - sections.set(section, matter); + if (!sections.compareAndSet(section, null, matter)) { + matter = get(section); + } } return matter; @@ -224,10 +266,15 @@ public class MantleChunk { dos.writeByte(x); dos.writeByte(z); dos.writeByte(sections.length()); - Varint.writeUnsignedVarInt(flags.length(), dos); + Varint.writeUnsignedVarInt(Math.ceilDiv(flags.length(), Byte.SIZE), dos); - for (int i = 0; i < flags.length(); i++) { - dos.writeBoolean(flags.get(i) == 1); + int count = flags.length(); + for (int i = 0; i < count;) { + int f = 0; + for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) { + f |= flags.get(i) ? (1 << j) : 0; + } + dos.write(f); } var bytes = new ByteArrayOutputStream(8192); diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java deleted file mode 100644 index 81c4a8504..000000000 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.util.mantle; - -import com.volmit.iris.util.collection.StateList; - -public enum MantleFlag { - OBJECT, - UPDATE, - JIGSAW, - FEATURE, - INITIAL_SPAWNED, - REAL, - CARVED, - FLUID_BODIES, - INITIAL_SPAWNED_MARKER, - CLEANED, - PLANNED, - ETCHED, - TILE, - CUSTOM, - DISCOVERED, - CUSTOM_ACTIVE; - - static StateList getStateList() { - return new StateList(MantleFlag.values()); - } -} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index 181ce9cd2..1b9ea817e 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -19,26 +19,14 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.util.data.Varint; 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.io.CountingDataInputStream; -import com.volmit.iris.util.io.IO; -import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Getter; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; import java.io.*; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -49,7 +37,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray; public class TectonicPlate { private static final ThreadLocal errors = ThreadLocal.withInitial(() -> false); public static final int MISSING = -1; - public static final int CURRENT = 0; + public static final int CURRENT = 1; private final int sectionHeight; private final AtomicReferenceArray chunks; @@ -111,31 +99,6 @@ public class TectonicPlate { } } - public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException { - try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { - fc.lock(); - - InputStream fin = Channels.newInputStream(fc); - LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); - BufferedInputStream bis = new BufferedInputStream(lz4); - try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) { - return new TectonicPlate(worldHeight, din, versioned); - } - } finally { - if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) { - File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin"); - try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { - fc.lock(); - - InputStream fin = Channels.newInputStream(fc); - LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); - Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - } - errors.remove(); - } - } - public boolean inUse() { for (int i = 0; i < chunks.length(); i++) { MantleChunk chunk = chunks.get(i); @@ -223,18 +186,6 @@ public class TectonicPlate { return Cache.to1D(x, z, 0, 32, 32); } - /** - * Write this tectonic plate to file - * - * @param file the file to writeNodeData it to - * @throws IOException shit happens - */ - public void write(File file) throws IOException { - PrecisionStopwatch p = PrecisionStopwatch.start(); - IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write); - Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); - } - /** * Write this tectonic plate to a data stream * @@ -268,4 +219,12 @@ public class TectonicPlate { public static void addError() { errors.set(true); } + + public static boolean hasError() { + try { + return errors.get(); + } finally { + errors.remove(); + } + } } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/flag/CustomFlag.java b/core/src/main/java/com/volmit/iris/util/mantle/flag/CustomFlag.java new file mode 100644 index 000000000..5b239e1a1 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/flag/CustomFlag.java @@ -0,0 +1,28 @@ +package com.volmit.iris.util.mantle.flag; + +import org.jetbrains.annotations.NotNull; + +record CustomFlag(String name, int ordinal) implements MantleFlag { + + @Override + public @NotNull String toString() { + return name; + } + + @Override + public boolean isCustom() { + return false; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof CustomFlag that)) + return false; + return ordinal == that.ordinal; + } + + @Override + public int hashCode() { + return ordinal; + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/flag/MantleFlag.java b/core/src/main/java/com/volmit/iris/util/mantle/flag/MantleFlag.java new file mode 100644 index 000000000..965bf6780 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/flag/MantleFlag.java @@ -0,0 +1,57 @@ +/* + * 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.util.mantle.flag; + +import org.jetbrains.annotations.Contract; + +public sealed interface MantleFlag permits CustomFlag, ReservedFlag { + int MIN_ORDINAL = 64; + int MAX_ORDINAL = 255; + + MantleFlag OBJECT = ReservedFlag.OBJECT; + MantleFlag UPDATE = ReservedFlag.UPDATE; + MantleFlag JIGSAW = ReservedFlag.JIGSAW; + MantleFlag FEATURE = ReservedFlag.FEATURE; + MantleFlag INITIAL_SPAWNED = ReservedFlag.INITIAL_SPAWNED; + MantleFlag REAL = ReservedFlag.REAL; + MantleFlag CARVED = ReservedFlag.CARVED; + MantleFlag FLUID_BODIES = ReservedFlag.FLUID_BODIES; + MantleFlag INITIAL_SPAWNED_MARKER = ReservedFlag.INITIAL_SPAWNED_MARKER; + MantleFlag CLEANED = ReservedFlag.CLEANED; + MantleFlag PLANNED = ReservedFlag.PLANNED; + MantleFlag ETCHED = ReservedFlag.ETCHED; + MantleFlag TILE = ReservedFlag.TILE; + MantleFlag CUSTOM = ReservedFlag.CUSTOM; + MantleFlag DISCOVERED = ReservedFlag.DISCOVERED; + MantleFlag CUSTOM_ACTIVE = ReservedFlag.CUSTOM_ACTIVE; + + int RESERVED_FLAGS = ReservedFlag.values().length; + + String name(); + int ordinal(); + + boolean isCustom(); + + @Contract(value = "_ -> new", pure = true) + static MantleFlag of(int ordinal) { + if (ordinal < MIN_ORDINAL || ordinal > MAX_ORDINAL) + throw new IllegalArgumentException("Ordinal must be between " + MIN_ORDINAL + " and " + MAX_ORDINAL); + return new CustomFlag("CUSTOM:"+ordinal, ordinal); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/flag/MantleFlagAdapter.java b/core/src/main/java/com/volmit/iris/util/mantle/flag/MantleFlagAdapter.java new file mode 100644 index 000000000..c56916df8 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/flag/MantleFlagAdapter.java @@ -0,0 +1,31 @@ +package com.volmit.iris.util.mantle.flag; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; + +public class MantleFlagAdapter extends TypeAdapter { + private static final String CUSTOM = "CUSTOM:"; + private static final int CUSTOM_LENGTH = CUSTOM.length(); + + @Override + public void write(JsonWriter out, MantleFlag value) throws IOException { + if (value == null) out.nullValue(); + else out.value(value.toString()); + } + + @Override + public MantleFlag read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String s = in.nextString(); + if (s.startsWith(CUSTOM) && s.length() > CUSTOM_LENGTH) + return MantleFlag.of(Integer.parseInt(s.substring(CUSTOM_LENGTH))); + return ReservedFlag.valueOf(s); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/flag/ReservedFlag.java b/core/src/main/java/com/volmit/iris/util/mantle/flag/ReservedFlag.java new file mode 100644 index 000000000..c6ea07b64 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/flag/ReservedFlag.java @@ -0,0 +1,25 @@ +package com.volmit.iris.util.mantle.flag; + +public enum ReservedFlag implements MantleFlag { + OBJECT, + UPDATE, + JIGSAW, + FEATURE, + INITIAL_SPAWNED, + REAL, + CARVED, + FLUID_BODIES, + INITIAL_SPAWNED_MARKER, + CLEANED, + PLANNED, + ETCHED, + TILE, + CUSTOM, + DISCOVERED, + CUSTOM_ACTIVE; + + @Override + public boolean isCustom() { + return false; + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/DelegateStream.java b/core/src/main/java/com/volmit/iris/util/mantle/io/DelegateStream.java new file mode 100644 index 000000000..2ccbbbe14 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/DelegateStream.java @@ -0,0 +1,123 @@ +/* + * 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.util.mantle.io; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; + +public class DelegateStream { + + public static InputStream read(FileChannel channel) throws IOException { + channel.position(0); + return new Input(channel); + } + + public static OutputStream write(FileChannel channel) throws IOException { + channel.position(0); + return new Output(channel); + } + + private static class Input extends InputStream { + private final InputStream delegate; + + private Input(FileChannel channel) { + this.delegate = Channels.newInputStream(channel); + } + + @Override + public int available() throws IOException { + return delegate.available(); + } + + @Override + public int read() throws IOException { + return delegate.read(); + } + + @Override + public int read(byte @NotNull [] b, int off, int len) throws IOException { + return delegate.read(b, off, len); + } + + @Override + public byte @NotNull [] readAllBytes() throws IOException { + return delegate.readAllBytes(); + } + + @Override + public int readNBytes(byte[] b, int off, int len) throws IOException { + return delegate.readNBytes(b, off, len); + } + + @Override + public byte @NotNull [] readNBytes(int len) throws IOException { + return delegate.readNBytes(len); + } + + @Override + public long skip(long n) throws IOException { + return delegate.skip(n); + } + + @Override + public void skipNBytes(long n) throws IOException { + delegate.skipNBytes(n); + } + + @Override + public long transferTo(OutputStream out) throws IOException { + return delegate.transferTo(out); + } + } + + private static class Output extends OutputStream { + private final FileChannel channel; + private final OutputStream delegate; + + private Output(FileChannel channel) { + this.channel = channel; + this.delegate = Channels.newOutputStream(channel); + } + + @Override + public void write(int b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte @NotNull [] b, int off, int len) throws IOException { + delegate.write(b, off, len); + } + + @Override + public void flush() throws IOException { + channel.truncate(channel.position()); + } + + @Override + public void close() throws IOException { + channel.force(true); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/Holder.java b/core/src/main/java/com/volmit/iris/util/mantle/io/Holder.java new file mode 100644 index 000000000..fa36ce9c5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/Holder.java @@ -0,0 +1,57 @@ +/* + * 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.util.mantle.io; + +import com.volmit.iris.util.io.IO; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.util.concurrent.Semaphore; + +class Holder { + private final FileChannel channel; + private final Semaphore semaphore = new Semaphore(1); + private volatile boolean closed; + + Holder(FileChannel channel) throws IOException { + this.channel = channel; + IO.lock(channel); + } + + SynchronizedChannel acquire() { + semaphore.acquireUninterruptibly(); + if (closed) { + semaphore.release(); + return null; + } + + return new SynchronizedChannel(channel, semaphore); + } + + void close() throws IOException { + semaphore.acquireUninterruptibly(); + try { + if (closed) return; + closed = true; + channel.close(); + } finally { + semaphore.release(); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java b/core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java new file mode 100644 index 000000000..d2edb4995 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java @@ -0,0 +1,130 @@ +/* + * 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.util.mantle.io; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.io.CountingDataInputStream; +import com.volmit.iris.util.mantle.TectonicPlate; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; + +import java.io.*; +import java.nio.channels.FileChannel; +import java.nio.file.*; +import java.util.Objects; +import java.util.Set; + +public class IOWorker { + private static final Set OPTIONS = Set.of(StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.SYNC); + private static final int MAX_CACHE_SIZE = 128; + + private final Path root; + private final File tmp; + private final int worldHeight; + + private final Object2ObjectLinkedOpenHashMap cache = new Object2ObjectLinkedOpenHashMap<>(); + + public IOWorker(File root, int worldHeight) { + this.root = root.toPath(); + this.tmp = new File(root, ".tmp"); + this.worldHeight = worldHeight; + } + + public TectonicPlate read(final String name) throws IOException { + PrecisionStopwatch p = PrecisionStopwatch.start(); + try (var channel = getChannel(name)) { + var raw = channel.read(); + var lz4 = new LZ4BlockInputStream(raw); + var buffered = new BufferedInputStream(lz4); + try (var in = CountingDataInputStream.wrap(buffered)) { + return new TectonicPlate(worldHeight, in, name.startsWith("pv.")); + } finally { + if (TectonicPlate.hasError() && IrisSettings.get().getGeneral().isDumpMantleOnError()) { + File dump = Iris.instance.getDataFolder("dump", name + ".bin"); + Files.copy(new LZ4BlockInputStream(channel.read()), dump.toPath(), StandardCopyOption.REPLACE_EXISTING); + } else { + Iris.debug("Read Tectonic Plate " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + } + } + } + } + + public void write(final String name, final TectonicPlate plate) throws IOException { + PrecisionStopwatch p = PrecisionStopwatch.start(); + try (var channel = getChannel(name)) { + tmp.mkdirs(); + File file = File.createTempFile("iris", ".bin", tmp); + try { + try (var tmp = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) { + plate.write(tmp); + } + + try (var out = channel.write()) { + Files.copy(file.toPath(), out); + out.flush(); + } + } finally { + file.delete(); + } + } + Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + } + + public void close() throws IOException { + synchronized (cache) { + for (Holder h : cache.values()) { + h.close(); + } + + cache.clear(); + } + } + + private SynchronizedChannel getChannel(final String name) throws IOException { + PrecisionStopwatch p = PrecisionStopwatch.start(); + try { + synchronized (cache) { + Holder holder = cache.getAndMoveToFirst(name); + if (holder != null) { + var channel = holder.acquire(); + if (channel != null) { + return channel; + } + } + + if (cache.size() >= MAX_CACHE_SIZE) { + var last = cache.removeLast(); + last.close(); + } + + + holder = new Holder(FileChannel.open(root.resolve(name), OPTIONS)); + cache.putAndMoveToFirst(name, holder); + return Objects.requireNonNull(holder.acquire()); + } + } finally { + Iris.debug("Acquired Channel for " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/SynchronizedChannel.java b/core/src/main/java/com/volmit/iris/util/mantle/io/SynchronizedChannel.java new file mode 100644 index 000000000..a26d02967 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/SynchronizedChannel.java @@ -0,0 +1,54 @@ +/* + * 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.util.mantle.io; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.util.concurrent.Semaphore; + +public class SynchronizedChannel implements Closeable { + private final FileChannel channel; + private final Semaphore lock; + private transient boolean closed; + + SynchronizedChannel(FileChannel channel, Semaphore lock) { + this.channel = channel; + this.lock = lock; + } + + public InputStream read() throws IOException { + if (closed) throw new IOException("Channel is closed!"); + return DelegateStream.read(channel); + } + + public OutputStream write() throws IOException { + if (closed) throw new IOException("Channel is closed!"); + return DelegateStream.write(channel); + } + + @Override + public void close() throws IOException { + if (closed) return; + closed = true; + lock.release(); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java b/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java index d645df076..233a9173e 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java @@ -25,6 +25,8 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.plugin.VolmitSender; import lombok.Getter; +import java.util.Objects; + public class IrisMatter extends IrisRegistrant implements Matter { protected static final KMap, MatterSlice> slicers = buildSlicers(); @@ -65,6 +67,12 @@ public class IrisMatter extends IrisRegistrant implements Matter { return c; } + @Override + @SuppressWarnings("unchecked") + public MatterSlice slice(Class c) { + return (MatterSlice) sliceMap.computeIfAbsent(c, $ -> Objects.requireNonNull(createSlice(c, this), "Bad slice " + c.getCanonicalName())); + } + @Override public MatterSlice createSlice(Class type, Matter m) { MatterSlice slice = slicers.get(type); diff --git a/core/src/main/java/com/volmit/iris/util/matter/Matter.java b/core/src/main/java/com/volmit/iris/util/matter/Matter.java index 086b397bd..0fe0dca0c 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/Matter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/Matter.java @@ -99,10 +99,9 @@ public interface Matter { } static Matter read(File f) throws IOException { - FileInputStream in = new FileInputStream(f); - Matter m = read(in); - in.close(); - return m; + try (var in = new FileInputStream(f)) { + return read(in); + } } static Matter read(InputStream in) throws IOException { @@ -142,6 +141,7 @@ public interface Matter { long size = din.readInt(); if (size == 0) continue; long start = din.count(); + long end = start + size; Iris.addPanic("read.matter.slice", i + ""); try { @@ -151,9 +151,9 @@ public interface Matter { Class type = Class.forName(cn); MatterSlice slice = matter.createSlice(type, matter); slice.read(din); + if (din.count() < end) throw new IOException("Matter slice read size mismatch!"); matter.putSlice(type, slice); } catch (Throwable e) { - long end = start + size; if (!(e instanceof ClassNotFoundException)) { Iris.error("Failed to read matter slice, skipping it."); Iris.addPanic("read.byte.range", start + " " + end); @@ -165,6 +165,10 @@ public interface Matter { } din.skipTo(end); } + + if (din.count() != end) { + throw new IOException("Matter slice read size mismatch!"); + } } return matter; diff --git a/core/src/main/java/com/volmit/iris/util/misc/Bindings.java b/core/src/main/java/com/volmit/iris/util/misc/Bindings.java index 6dea9f4a8..1a82c55c4 100644 --- a/core/src/main/java/com/volmit/iris/util/misc/Bindings.java +++ b/core/src/main/java/com/volmit/iris/util/misc/Bindings.java @@ -51,7 +51,7 @@ public class Bindings { YamlConfiguration desc = resource != null ? YamlConfiguration.loadConfiguration(new InputStreamReader(resource)) : new YamlConfiguration(); Sentry.init(options -> { - options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); + options.setDsn("http://4cdbb9ac953306529947f4ca1e8e6b26@sentry.volmit.com:8080/2"); if (settings.debug) { options.setLogger(new IrisLogger()); options.setDebug(true); @@ -79,7 +79,6 @@ public class Bindings { scope.setTag("server.api", Bukkit.getBukkitVersion()); scope.setTag("iris.commit", desc.getString("commit", "unknown")); }); - Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); } private static boolean suppress(Throwable e) { diff --git a/core/src/main/java/com/volmit/iris/util/noise/NoiseGenerator.java b/core/src/main/java/com/volmit/iris/util/noise/NoiseGenerator.java index 6ad166239..4574e9eb1 100644 --- a/core/src/main/java/com/volmit/iris/util/noise/NoiseGenerator.java +++ b/core/src/main/java/com/volmit/iris/util/noise/NoiseGenerator.java @@ -39,4 +39,8 @@ public interface NoiseGenerator { default ProceduralStream stream() { return ProceduralStream.of(this::noise, this::noise, Interpolated.DOUBLE); } + + default OffsetNoiseGenerator offset(long seed) { + return new OffsetNoiseGenerator(this, seed); + } } diff --git a/core/src/main/java/com/volmit/iris/util/noise/NoiseType.java b/core/src/main/java/com/volmit/iris/util/noise/NoiseType.java index b2d26b2c8..20f89ca8b 100644 --- a/core/src/main/java/com/volmit/iris/util/noise/NoiseType.java +++ b/core/src/main/java/com/volmit/iris/util/noise/NoiseType.java @@ -18,6 +18,7 @@ package com.volmit.iris.util.noise; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.util.interpolation.InterpolationMethod; public enum NoiseType { @@ -77,6 +78,7 @@ public enum NoiseType { } public NoiseGenerator create(long seed) { - return f.create(seed); + if (IrisSettings.get().getGenerator().offsetNoiseTypes) return f.create(seed).offset(seed); + else return f.create(seed); } } diff --git a/core/src/main/java/com/volmit/iris/util/noise/OffsetNoiseGenerator.java b/core/src/main/java/com/volmit/iris/util/noise/OffsetNoiseGenerator.java new file mode 100644 index 000000000..97e3088c0 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/noise/OffsetNoiseGenerator.java @@ -0,0 +1,48 @@ +package com.volmit.iris.util.noise; + + +import com.volmit.iris.util.math.RNG; +import org.jetbrains.annotations.NotNull; + + +public class OffsetNoiseGenerator implements NoiseGenerator { + private final NoiseGenerator base; + private final double ox, oz; + + public OffsetNoiseGenerator(NoiseGenerator base, long seed) { + this.base = base; + RNG rng = new RNG(seed); + ox = rng.nextInt(Short.MIN_VALUE, Short.MAX_VALUE); + oz = rng.nextInt(Short.MIN_VALUE, Short.MAX_VALUE); + } + + @Override + public double noise(double x) { + return base.noise(x + ox); + } + + @Override + public double noise(double x, double z) { + return base.noise(x + ox, z + oz); + } + + @Override + public double noise(double x, double y, double z) { + return base.noise(x + ox, y, z + oz); + } + + @Override + public boolean isNoScale() { + return base.isNoScale(); + } + + @Override + public boolean isStatic() { + return base.isStatic(); + } + + @NotNull + public NoiseGenerator getBase() { + return base; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/parallel/AtomicBooleanArray.java b/core/src/main/java/com/volmit/iris/util/parallel/AtomicBooleanArray.java new file mode 100644 index 000000000..5d2603a85 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/parallel/AtomicBooleanArray.java @@ -0,0 +1,46 @@ +package com.volmit.iris.util.parallel; + +import java.io.Serializable; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + +public class AtomicBooleanArray implements Serializable { + private static final VarHandle AA = MethodHandles.arrayElementVarHandle(boolean[].class); + private final boolean[] array; + + public AtomicBooleanArray(int length) { + array = new boolean[length]; + } + + public final int length() { + return array.length; + } + + public final boolean get(int index) { + return (boolean) AA.getVolatile(array, index); + } + + public final void set(int index, boolean newValue) { + AA.setVolatile(array, index, newValue); + } + + public final boolean compareAndSet(int index, boolean expectedValue, boolean newValue) { + return (boolean) AA.compareAndSet(array, index, expectedValue, newValue); + } + + @Override + public String toString() { + int iMax = array.length - 1; + if (iMax == -1) + return "[]"; + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + b.append(get(i)); + if (i == iMax) + return b.append(']').toString(); + b.append(',').append(' '); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java b/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java index 6d0eaaee0..158a55e9d 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java @@ -22,6 +22,7 @@ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.volmit.iris.Iris; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.util.function.NastyRunnable; +import com.volmit.iris.util.function.NastySupplier; import com.volmit.iris.util.io.IORunnable; import java.io.IOException; @@ -58,14 +59,21 @@ public class HyperLock { public void with(int x, int z, Runnable r) { lock(x, z); - r.run(); - unlock(x, z); + try { + r.run(); + } finally { + unlock(x, z); + } } public void withLong(long k, Runnable r) { - lock(Cache.keyX(k), Cache.keyZ(k)); - r.run(); - unlock(Cache.keyX(k), Cache.keyZ(k)); + int x = Cache.keyX(k), z = Cache.keyZ(k); + lock(x, z); + try { + r.run(); + } finally { + unlock(x, z); + } } public void withNasty(int x, int z, NastyRunnable r) throws Throwable { @@ -102,9 +110,20 @@ public class HyperLock { public T withResult(int x, int z, Supplier r) { lock(x, z); - T t = r.get(); - unlock(x, z); - return t; + try { + return r.get(); + } finally { + unlock(x, z); + } + } + + public T withNastyResult(int x, int z, NastySupplier r) throws Throwable { + lock(x, z); + try { + return r.get(); + } finally { + unlock(x, z); + } } public boolean tryLock(int x, int z) { diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index 500a4a0b2..32ab14103 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -20,7 +20,6 @@ package com.volmit.iris.util.parallel; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.service.PreservationSVC; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.math.M; import com.volmit.iris.util.scheduling.PrecisionStopwatch; @@ -30,30 +29,41 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.IntSupplier; public class MultiBurst implements ExecutorService { private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000); public static final MultiBurst burst = new MultiBurst(); + public static final MultiBurst ioBurst = new MultiBurst("Iris IO", () -> IrisSettings.get().getConcurrency().getIoParallelism()); private final AtomicLong last; private final String name; private final int priority; + private final IntSupplier parallelism; private ExecutorService service; public MultiBurst() { - this("Iris", Thread.MIN_PRIORITY); + this("Iris"); } - public MultiBurst(String name, int priority) { + public MultiBurst(String name) { + this(name, Thread.MIN_PRIORITY, () -> IrisSettings.get().getConcurrency().getParallelism()); + } + + public MultiBurst(String name, IntSupplier parallelism) { + this(name, Thread.MIN_PRIORITY, parallelism); + } + + public MultiBurst(String name, int priority, IntSupplier parallelism) { this.name = name; this.priority = priority; + this.parallelism = parallelism; last = new AtomicLong(M.ms()); - Iris.service(PreservationSVC.class).register(this); } private synchronized ExecutorService getService() { last.set(M.ms()); if (service == null || service.isShutdown()) { - service = new ForkJoinPool(IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()), + service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()), new ForkJoinPool.ForkJoinWorkerThreadFactory() { int m = 0; diff --git a/core/src/main/java/com/volmit/iris/util/parallel/StreamUtils.java b/core/src/main/java/com/volmit/iris/util/parallel/StreamUtils.java new file mode 100644 index 000000000..dad656936 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/parallel/StreamUtils.java @@ -0,0 +1,39 @@ +package com.volmit.iris.util.parallel; + +import com.volmit.iris.util.math.Position2; +import lombok.SneakyThrows; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class StreamUtils { + + public static Stream streamRadius(int x, int z, int radius) { + return streamRadius(x, z, radius, radius); + } + + public static Stream streamRadius(int x, int z, int radiusX, int radiusZ) { + return IntStream.rangeClosed(-radiusX, radiusX) + .mapToObj(xx -> IntStream.rangeClosed(-radiusZ, radiusZ) + .mapToObj(zz -> new Position2(x + xx, z + zz))) + .flatMap(Function.identity()); + } + + public static void forEach(Stream stream, Function> mapper, Consumer consumer, @Nullable MultiBurst burst) { + forEach(stream.flatMap(mapper), consumer, burst); + } + + @SneakyThrows + public static void forEach(Stream stream, Consumer task, @Nullable MultiBurst burst) { + if (burst == null) stream.forEach(task); + else { + var list = stream.toList(); + var exec = burst.burst(list.size()); + list.forEach(val -> exec.queue(() -> task.accept(val))); + exec.complete(); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/stream/ProceduralStream.java b/core/src/main/java/com/volmit/iris/util/stream/ProceduralStream.java index e860f299a..539a0f224 100644 --- a/core/src/main/java/com/volmit/iris/util/stream/ProceduralStream.java +++ b/core/src/main/java/com/volmit/iris/util/stream/ProceduralStream.java @@ -336,6 +336,7 @@ public interface ProceduralStream extends ProceduralLayer, Interpolated { } @SuppressWarnings("unchecked") + @Deprecated(forRemoval = true) default ProceduralStream selectRarity(V... types) { KList rarityTypes = new KList<>(); int totalRarity = 0; @@ -350,16 +351,16 @@ public interface ProceduralStream extends ProceduralLayer, Interpolated { return new SelectionStream(this, rarityTypes); } - default ProceduralStream selectRarity(List types) { - return IRare.stream(this.forceDouble(), types); + default ProceduralStream selectRarity(List types, boolean legacy) { + return IRare.stream(this.forceDouble(), types, legacy); } - default ProceduralStream selectRarity(List types, Function loader) { + default ProceduralStream selectRarity(List types, Function loader, boolean legacy) { List r = new ArrayList<>(); for (V f : types) { r.add(loader.apply(f)); } - return selectRarity(r); + return selectRarity(r, legacy); } default int countPossibilities(List types, Function loader) { diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/DataScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/DataScript.kt new file mode 100644 index 000000000..1a180701c --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/DataScript.kt @@ -0,0 +1,15 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import com.volmit.iris.core.loader.IrisData +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.providedProperties + +@KotlinScript(fileExtension = "data.kts", compilationConfiguration = DataScriptDefinition::class) +abstract class DataScript + +object DataScriptDefinition : ScriptCompilationConfiguration(listOf(SimpleScriptDefinition), { + providedProperties("data" to IrisData::class) +}) { + private fun readResolve(): Any = DataScriptDefinition +} \ No newline at end of file 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 new file mode 100644 index 000000000..768f2a9d3 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/EngineScript.kt @@ -0,0 +1,25 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import com.volmit.iris.core.scripting.func.BiomeLookup +import com.volmit.iris.engine.IrisComplex +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 + +@KotlinScript(fileExtension = "engine.kts", compilationConfiguration = EngineScriptDefinition::class) +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, + "biome" to BiomeLookup::class, + ) +}) { + + private fun readResolve(): Any = EngineScriptDefinition +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/MobSpawningScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/MobSpawningScript.kt new file mode 100644 index 000000000..f9add58dc --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/MobSpawningScript.kt @@ -0,0 +1,15 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import org.bukkit.Location +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.providedProperties + +@KotlinScript(fileExtension = "spawn.kts", compilationConfiguration = MobSpawningScriptDefinition::class) +abstract class MobSpawningScript + +object MobSpawningScriptDefinition : ScriptCompilationConfiguration(listOf(EngineScriptDefinition), { + providedProperties("location" to Location::class) +}) { + private fun readResolve(): Any = MobSpawningScriptDefinition +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/NoiseScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/NoiseScript.kt new file mode 100644 index 000000000..c8a7056fa --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/NoiseScript.kt @@ -0,0 +1,16 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import com.volmit.iris.util.math.RNG +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.providedProperties + +@KotlinScript(fileExtension = "noise.kts", compilationConfiguration = NoiseScriptDefinition::class) +abstract class NoiseScript + +object NoiseScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), { + providedProperties("rng" to RNG::class) +}) { + + private fun readResolve(): Any = NoiseScriptDefinition +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PostMobSpawningScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PostMobSpawningScript.kt new file mode 100644 index 000000000..02b485bed --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PostMobSpawningScript.kt @@ -0,0 +1,15 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import org.bukkit.entity.Entity +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.providedProperties + +@KotlinScript(fileExtension = "postspawn.kts", compilationConfiguration = PostMobSpawningScriptDefinition::class) +abstract class PostMobSpawningScript + +object PostMobSpawningScriptDefinition : ScriptCompilationConfiguration(listOf(MobSpawningScriptDefinition), { + providedProperties("entity" to Entity::class) +}) { + private fun readResolve(): Any = PostMobSpawningScriptDefinition +} \ No newline at end of file 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 new file mode 100644 index 000000000..f594649e1 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/PreprocessorScript.kt @@ -0,0 +1,15 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import com.volmit.iris.core.loader.IrisRegistrant +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +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) +}) { + private fun readResolve(): Any = PreprocessorScriptDefinition +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/SimpleScript.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/SimpleScript.kt new file mode 100644 index 000000000..5839bbcc0 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/base/SimpleScript.kt @@ -0,0 +1,33 @@ +package com.volmit.iris.core.scripting.kotlin.base + +import com.volmit.iris.core.scripting.kotlin.runner.configure +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.api.defaultImports +import kotlin.script.experimental.dependencies.DependsOn +import kotlin.script.experimental.dependencies.Repository +import kotlin.script.experimental.jvm.dependenciesFromClassContext +import kotlin.script.experimental.jvm.jvm + +@KotlinScript(fileExtension = "simple.kts", compilationConfiguration = SimpleScriptDefinition::class) +abstract class SimpleScript + +object SimpleScriptDefinition : ScriptCompilationConfiguration({ + defaultImports( + DependsOn::class.qualifiedName!!, + Repository::class.qualifiedName!!, + "com.volmit.iris.Iris.info", + "com.volmit.iris.Iris.debug", + "com.volmit.iris.Iris.warn", + "com.volmit.iris.Iris.error" + ) + + jvm { + dependenciesFromClassContext(KotlinScript::class, wholeClasspath = true) + dependenciesFromClassContext(SimpleScript::class, wholeClasspath = true) + } + + configure() +}) { + private fun readResolve(): Any = SimpleScriptDefinition +} \ 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 new file mode 100644 index 000000000..edaba44e5 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisExecutionEnvironment.kt @@ -0,0 +1,45 @@ +package com.volmit.iris.core.scripting.kotlin.environment + +import com.volmit.iris.core.loader.IrisRegistrant +import com.volmit.iris.core.scripting.environment.EngineEnvironment +import com.volmit.iris.core.scripting.func.BiomeLookup +import com.volmit.iris.core.scripting.kotlin.base.EngineScript +import com.volmit.iris.core.scripting.kotlin.base.MobSpawningScript +import com.volmit.iris.core.scripting.kotlin.base.PostMobSpawningScript +import com.volmit.iris.core.scripting.kotlin.base.PreprocessorScript +import com.volmit.iris.engine.framework.Engine +import org.bukkit.Location +import org.bukkit.entity.Entity + +data class IrisExecutionEnvironment( + private val engine: Engine +) : IrisPackExecutionEnvironment(engine.data), EngineEnvironment { + override fun getEngine() = engine + + override fun execute(script: String) = + execute(script, EngineScript::class.java, engine.parameters()) + + override fun evaluate(script: String) = + evaluate(script, EngineScript::class.java, engine.parameters()) + + override fun spawnMob(script: String, location: Location) = + evaluate(script, MobSpawningScript::class.java, engine.parameters("location" to location)) + + override fun postSpawnMob(script: String, location: Location, mob: Entity) = + 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`)) + + private fun Engine.parameters(vararg values: Pair): Map { + return mapOf( + "data" to data, + "engine" to this, + "complex" to complex, + "seed" to seedManager.seed, + "dimension" to dimension, + "biome" to BiomeLookup(::getSurfaceBiome), + *values, + ) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisPackExecutionEnvironment.kt b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisPackExecutionEnvironment.kt new file mode 100644 index 000000000..86832c963 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisPackExecutionEnvironment.kt @@ -0,0 +1,40 @@ +package com.volmit.iris.core.scripting.kotlin.environment + +import com.volmit.iris.core.loader.IrisData +import com.volmit.iris.core.scripting.environment.PackEnvironment +import com.volmit.iris.core.scripting.kotlin.base.DataScript +import com.volmit.iris.core.scripting.kotlin.base.NoiseScript +import com.volmit.iris.core.scripting.kotlin.runner.Script +import com.volmit.iris.core.scripting.kotlin.runner.valueOrThrow +import com.volmit.iris.util.math.RNG +import kotlin.reflect.KClass + +open class IrisPackExecutionEnvironment( + private val data: IrisData +) : IrisSimpleExecutionEnvironment(data.dataFolder), PackEnvironment { + + override fun getData() = data + + override fun compile(script: String, type: KClass<*>): Script { + val loaded = data.scriptLoader.load(script) + return compileCache.get(script) + .computeIfAbsent(type) { _ -> runner.compile(type, loaded.loadFile, loaded.source) } + .valueOrThrow("Failed to compile script $script") + } + + override fun execute(script: String) = + execute(script, DataScript::class.java, data.parameters()) + + override fun evaluate(script: String) = + evaluate(script, DataScript::class.java, data.parameters()) + + override fun createNoise(script: String, rng: RNG) = + evaluate(script, NoiseScript::class.java, data.parameters("rng" to rng)) + + private fun IrisData.parameters(vararg values: Pair): Map { + return mapOf( + "data" to this, + *values, + ) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..2896b5821 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/core/scripting/kotlin/environment/IrisSimpleExecutionEnvironment.kt @@ -0,0 +1,141 @@ +package com.volmit.iris.core.scripting.kotlin.environment + +import com.volmit.iris.Iris +import com.volmit.iris.core.IrisSettings +import com.volmit.iris.core.scripting.environment.SimpleEnvironment +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.valueOrThrow +import com.volmit.iris.util.collection.KMap +import com.volmit.iris.util.data.KCache +import com.volmit.iris.util.format.C +import java.io.File +import kotlin.reflect.KClass +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.text.split + +open class IrisSimpleExecutionEnvironment( + baseDir: File = File(".").absoluteFile +) : SimpleEnvironment { + protected val compileCache = KCache, ResultWithDiagnostics