diff --git a/README.md b/README.md index 52dbc784e..fadd56ffe 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Consider supporting our development by buying Iris on spigot! We work hard to ma 4. Use `CTRL + X`, then Press `Y`, Then `ENTER` 5. Quit & Reopen Terminal and verify with `echo $JAVA_HOME`. It should print a directory 3. If this is your first time building Iris for MC 1.18+ run `gradlew setup` inside the root Iris project folder. - Otherwise, skip this step. Grab a coffee, this may take up to 5 minutes depending on your cpu & internet connection. + Otherwise, skip this step. Grab a coffee, this may take up to 45 minutes depending on your cpu & internet connection. 4. Once the project has setup, run `gradlew iris` 5. The Iris jar will be placed in `Iris/build/Iris-XXX-XXX.jar` Enjoy! Consider supporting us by buying it on spigot! @@ -69,7 +69,6 @@ IrisAccess access=IrisToolbelt.createWorld() // If you like builders... .name("myWorld") // The world name .dimension("terrifyinghands") .seed(69133742) // The world seed - .headless(true) // Headless make gen go fast .pregen(PregenTask // Define a pregen job to run .builder() .center(new Position2(0,0)) // REGION coords (1 region = 32x32 chunks) diff --git a/build.gradle b/build.gradle index ea7d53891..a797903d2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,3 @@ -import java.util.function.Consumer - /* * Iris is a World Generator for Minecraft Bukkit Servers * Copyright (c) 2021 Arcane Arts (Volmit Software) @@ -21,12 +19,12 @@ import java.util.function.Consumer plugins { id 'java' id 'java-library' - id "com.github.johnrengelman.shadow" version "7.1.2" + id "io.github.goooler.shadow" version "8.1.7" id "de.undercouch.download" version "5.0.1" } -version '3.0.0-1.19.2-1.20.4' -def specialSourceVersion = '1.11.0' //[NMS] +version '3.2.6-1.19.2-1.20.4' +def specialSourceVersion = '1.11.4' //[NMS] // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -37,13 +35,14 @@ registerCustomOutputTask('Coco', 'D://mcsm/plugins') registerCustomOutputTask('Strange', 'D://Servers/1.17 Test Server/plugins') registerCustomOutputTask('Vatuu', 'D://Minecraft/Servers/1.19.4/plugins') registerCustomOutputTask('CrazyDev22', 'C://Users/Julian/Desktop/server/plugins') -registerCustomOutputTask('Pixel', 'C://Users/repix/Iris Dimension Engine/1.20.1 - Iris Coding/plugins') +registerCustomOutputTask('Pixel', 'C://Users/repix/Iris Dimension Engine/1.20.4 - Development/plugins') // ========================== UNIX ============================== registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins') registerCustomOutputTaskUnix('PsychoLT', '/Volumes/PRO-G40/Minecraft/MinecraftDevelopment/Server/plugins') // ============================================================== def NMS_BINDINGS = Map.of( + "v1_20_R4", "1.20.6-R0.1-SNAPSHOT", "v1_20_R3", "1.20.4-R0.1-SNAPSHOT", "v1_20_R2", "1.20.2-R0.1-SNAPSHOT", "v1_20_R1", "1.20.1-R0.1-SNAPSHOT", @@ -51,11 +50,15 @@ def NMS_BINDINGS = Map.of( "v1_19_R2", "1.19.3-R0.1-SNAPSHOT", "v1_19_R1", "1.19.2-R0.1-SNAPSHOT" ) +def JVM_VERSION = Map.of( + "v1_20_R4", 21, +) NMS_BINDINGS.each { def key = it.key def value = it.value def nms = value.split("-")[0]; project(":nms:${key}") { + apply plugin: 'java' apply plugin: 'java-library' apply plugin: 'de.undercouch.download' @@ -64,9 +67,10 @@ NMS_BINDINGS.each { compileOnly "org.spigotmc:spigot-api:${value}" compileOnly "org.bukkit:craftbukkit:${value}:remapped-mojang" //[NMS] } - def buildToolsJar = new File(rootProject.buildDir, "tools/BuildTools.jar") - def specialSourceJar = new File(rootProject.buildDir, "tools/SpecialSource.jar") + def buildToolsJar = new File(rootProject.layout.buildDirectory.asFile.get(), "tools/BuildTools.jar") + def specialSourceJar = new File(rootProject.layout.buildDirectory.asFile.get(), "tools/SpecialSource.jar") + def buildDir = layout.buildDirectory.asFile.get(); def buildToolsFolder = new File(buildDir, "buildtools") def specialSourceFolder = new File(buildDir, "specialsource") def buildToolsHint = new File(buildDir, "buildtools/craftbukkit-" + nms + ".jar") @@ -81,6 +85,22 @@ NMS_BINDINGS.each { def m2s = m2.getAbsolutePath(); // ======================== Building Mapped Jars ============================= + def targetJavaVersion = JVM_VERSION.getOrDefault(key, 17) + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + def javaLanguageVersion = JavaLanguageVersion.of(targetJavaVersion) + project.java.sourceCompatibility = javaVersion + project.java.targetCompatibility = javaVersion + project.java.toolchain.languageVersion = javaLanguageVersion + def launcher = javaToolchains.launcherFor(java.toolchain).get() + def javaHome = launcher.executablePath.getAsFile().parentFile.parentFile.getAbsolutePath() + + tasks.withType(JavaCompile).configureEach { + options.release.set(targetJavaVersion) + } + + tasks.withType(JavaExec).configureEach { + javaLauncher.set(launcher) + } ext { executeBuildTools = new Runnable() { @@ -98,6 +118,7 @@ NMS_BINDINGS.each { if (!buildToolsHint.exists()) { buildToolsFolder.mkdirs() project.javaexec { + executable = launcher.executablePath classpath = files(buildToolsJar) workingDir = buildToolsFolder args = [ @@ -107,16 +128,14 @@ NMS_BINDINGS.each { "craftbukkit", "--remap" ] + def env = new HashMap(environment) + env.put("JAVA_HOME", javaHome) + environment = env } } } } } - tasks.register("executeBuildTools") { - doLast { - property("executeBuildTools").run(); - } - } tasks.build.doLast { //Download @@ -136,6 +155,7 @@ NMS_BINDINGS.each { //obfuscate javaexec { + executable = launcher.executablePath workingDir = specialSourceFolder classpath = files(specialSourceJar, new File(m2s + "/org/spigotmc/spigot/" + value + "/spigot-" + value + "-remapped-mojang.jar")) @@ -150,10 +170,14 @@ NMS_BINDINGS.each { m2s + "/org/spigotmc/minecraft-server/" + value + "/minecraft-server-" + value + "-maps-mojang.txt", "--reverse", ] + def env = new HashMap(environment) + env.put("JAVA_HOME", javaHome) + environment = env } //remap javaexec { + executable = launcher.executablePath workingDir = specialSourceFolder classpath = files(specialSourceJar, new File(m2s + "/org/spigotmc/spigot/" + value + "/spigot-" + value + "-remapped-obf.jar")) @@ -167,6 +191,9 @@ NMS_BINDINGS.each { "-m", m2s + "/org/spigotmc/minecraft-server/" + value + "/minecraft-server-" + value + "-maps-spigot.csrg" ] + def env = new HashMap(environment) + env.put("JAVA_HOME", javaHome) + environment = env } //copy copy { @@ -181,7 +208,10 @@ NMS_BINDINGS.each { } shadowJar { - NMS_BINDINGS.each {dependsOn(":nms:${it.key}:build")} + NMS_BINDINGS.each { + dependsOn(":nms:${it.key}:build") + from("${project(":nms:${it.key}").layout.buildDirectory.asFile.get()}/libs/${it.key}.jar") + } //minimize() append("plugin.yml") @@ -193,9 +223,6 @@ shadowJar { dependencies { implementation project(':core') - NMS_BINDINGS.each { - implementation project(":nms:${it.key}") - } } configurations.configureEach { @@ -235,8 +262,10 @@ allprojects { implementation 'com.dfsek:Paralithic:0.4.0' implementation 'io.papermc:paperlib:1.0.5' implementation "net.kyori:adventure-text-minimessage:4.13.1" - implementation 'net.kyori:adventure-platform-bukkit:4.3.0' + implementation 'net.kyori:adventure-platform-bukkit:4.3.2' implementation 'net.kyori:adventure-api:4.13.1' + //implementation 'org.bytedeco:javacpp:1.5.10' + //implementation 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10' compileOnly 'io.lumine:Mythic-Dist:5.2.1' // Dynamically Loaded @@ -244,9 +273,9 @@ allprojects { compileOnly 'it.unimi.dsi:fastutil:8.5.8' compileOnly 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' compileOnly 'org.zeroturnaround:zt-zip:1.14' - compileOnly 'com.google.code.gson:gson:2.9.0' + compileOnly 'com.google.code.gson:gson:2.10.1' compileOnly 'org.ow2.asm:asm:9.2' - compileOnly 'com.google.guava:guava:31.1-jre' + compileOnly 'com.google.guava:guava:33.0.0-jre' compileOnly 'bsf:bsf:2.4.0' compileOnly 'rhino:js:1.7R2' compileOnly 'com.github.ben-manes.caffeine:caffeine:3.0.6' @@ -282,8 +311,8 @@ if (JavaVersion.current().toString() != "17") { task iris(type: Copy) { group "iris" - from new File(buildDir, "libs/Iris-${version}.jar") - into buildDir + from new File(layout.buildDirectory.asFile.get(), "libs/Iris-${version}.jar") + into layout.buildDirectory.asFile.get() dependsOn(build) } @@ -306,7 +335,9 @@ NMS_BINDINGS.keySet().forEach { tasks.register("setup-${nms}") { group "iris" dependsOn(":nms:${nms}:clean") - dependsOn(":nms:${nms}:executeBuildTools") + doLast { + project(":nms:${nms}").property("executeBuildTools").run(); + } } } diff --git a/core/build.gradle b/core/build.gradle index 76d773096..d1c25950d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -19,7 +19,7 @@ plugins { id 'java' id 'java-library' - id "io.freefair.lombok" version "6.3.0" + id "io.freefair.lombok" version "8.6" } def apiVersion = '1.19' @@ -33,6 +33,11 @@ compileJava { options.encoding = "UTF-8" } +repositories { + maven { url 'https://nexus.phoenixdevt.fr/repository/maven-public/'} + maven { url 'https://repo.auxilor.io/repository/maven-public/' } +} + /** * Dependencies. * @@ -61,6 +66,8 @@ dependencies { compileOnly 'com.github.LoneDev6:api-itemsadder:3.4.1-r4' compileOnly 'com.github.PlaceholderAPI:placeholderapi:2.11.3' compileOnly 'com.github.Ssomar-Developement:SCore:4.23.10.8' + compileOnly 'net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT' + compileOnly 'com.willfp:EcoItems:5.44.0' //implementation files('libs/CustomItems.jar') } diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index a39306991..950913796 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -128,6 +128,10 @@ public class Iris extends VolmitPlugin implements Listener { private KMap, IrisService> services; public static VolmitSender getSender() { + if (sender == null) { + sender = new VolmitSender(Bukkit.getConsoleSender()); + sender.setTag(instance.getTag()); + } return sender; } @@ -194,10 +198,10 @@ public class Iris extends VolmitPlugin implements Listener { public static void msg(String string) { try { - sender.sendMessage(string); + getSender().sendMessage(string); } catch (Throwable e) { try { - System.out.println(instance.getTag() + string.replaceAll("(<([^>]+)>)", "")); + instance.getLogger().info(instance.getTag() + string.replaceAll("(<([^>]+)>)", "")); } catch (Throwable ignored1) { } @@ -364,6 +368,13 @@ public class Iris extends VolmitPlugin implements Listener { return Integer.parseInt(version); } + public static String getJava() { + String javaRuntimeName = System.getProperty("java.vm.name"); + String javaRuntimeVendor = System.getProperty("java.vendor"); + String javaRuntimeVersion = System.getProperty("java.vm.version"); + return String.format("%s %s (build %s)", javaRuntimeName, javaRuntimeVendor, javaRuntimeVersion); + } + public static void reportErrorChunk(int x, int z, Throwable e, String extra) { if (IrisSettings.get().getGeneral().isDebug()) { File f = instance.getDataFile("debug", "chunk-errors", "chunk." + x + "." + z + ".txt"); @@ -426,7 +437,7 @@ public class Iris extends VolmitPlugin implements Listener { } pw.close(); - System.out.println("DUMPED! See " + fi.getAbsolutePath()); + Iris.info("DUMPED! See " + fi.getAbsolutePath()); } catch (Throwable e) { e.printStackTrace(); } @@ -446,14 +457,12 @@ public class Iris extends VolmitPlugin implements Listener { private void enable() { instance = this; services = new KMap<>(); + setupAudience(); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); INMS.get(); IO.delete(new File("iris")); - setupAudience(); IrisSafeguard.IrisSafeguardSystem(); - sender = new VolmitSender(Bukkit.getConsoleSender()); - sender.setTag(getTag()); - instance = this; + getSender().setTag(getTag()); compat = IrisCompat.configured(getDataFile("compat.json")); linkMultiverseCore = new MultiverseCoreLink(); linkMythicMobs = new MythicMobsLink(); @@ -526,7 +535,7 @@ public class Iris extends VolmitPlugin implements Listener { Iris.info("Starting up auto Studio!"); try { Player r = new KList<>(getServer().getOnlinePlayers()).getRandom(); - Iris.service(StudioSVC.class).open(r != null ? new VolmitSender(r) : sender, 1337, IrisSettings.get().getGenerator().getDefaultWorldType(), (w) -> { + Iris.service(StudioSVC.class).open(r != null ? new VolmitSender(r) : getSender(), 1337, IrisSettings.get().getGenerator().getDefaultWorldType(), (w) -> { J.s(() -> { for (Player i : getServer().getOnlinePlayers()) { i.setGameMode(GameMode.SPECTATOR); @@ -727,10 +736,10 @@ public class Iris extends VolmitPlugin implements Listener { File ff = new File(w.worldFolder(), "iris/pack"); if (!ff.exists() || ff.listFiles().length == 0) { ff.mkdirs(); - service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), w.worldFolder()); + service(StudioSVC.class).installIntoWorld(getSender(), dim.getLoadKey(), w.worldFolder()); } - return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); + return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey(), false); } public void splash() { @@ -797,13 +806,11 @@ public class Iris extends VolmitPlugin implements Listener { } else { splash = splashstable; } - OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); - String osArch = osBean.getArch(); - String osName = osBean.getName(); if (!passedserversoftware) { Iris.info("Server type & version: " + C.RED + Bukkit.getVersion()); } else { Iris.info("Server type & version: " + Bukkit.getVersion()); } + Iris.info("Java: " + getJava()); if (!instance.getServer().getVersion().contains("Purpur")) { if (instance.getServer().getVersion().contains("Spigot") && instance.getServer().getVersion().contains("Bukkit")) { Iris.info(C.RED + " Iris requires paper or above to function properly.."); @@ -811,38 +818,11 @@ public class Iris extends VolmitPlugin implements Listener { Iris.info(C.YELLOW + "Purpur is recommended to use with iris."); } } - Iris.info("Server OS: " + osName + " (" + osArch + ")"); - - try { - if (warningmode){ - Iris.info("Server Cpu: " + C.GOLD + getCPUModel()); - } else { - if(unstablemode){ - Iris.info("Server Cpu: " + C.DARK_RED + getCPUModel()); - } else { - if (getCPUModel().contains("Intel")) { - Iris.info("Server Cpu: " + C.BLUE + getCPUModel()); - } - if (getCPUModel().contains("Ryzen")) { - Iris.info("Server Cpu: " + C.RED + getCPUModel()); - } - if (!getCPUModel().contains("Ryzen") && !getCPUModel().contains("Intel")) { - Iris.info("Server Cpu: " + C.GRAY + getCPUModel()); - } - } - } - } catch (Exception e){ - Iris.info("Server Cpu: " + C.DARK_RED + "Failed"); - } - - Iris.info("Process Threads: " + Runtime.getRuntime().availableProcessors()); - Iris.info("Process Memory: " + getHardware.getProcessMemory() + " MB"); - Iris.info("Free DiskSpace: " + Form.ofSize(freeSpace.getFreeSpace(), 1024)); if (getHardware.getProcessMemory() < 5999) { Iris.warn("6GB+ Ram is recommended"); + Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB"); } Iris.info("Bukkit version: " + Bukkit.getBukkitVersion()); - Iris.info("Java version: " + getJavaVersion()); Iris.info("Custom Biomes: " + INMS.get().countCustomBiomes()); setupChecks(); printPacks(); 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 ea17736ab..335207de5 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -24,6 +24,7 @@ import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.ChronoLatch; import lombok.Data; import java.io.File; @@ -135,7 +136,7 @@ public class IrisSettings { @Data public static class IrisSettingsPerformance { - public boolean trimMantleInStudio = false; + public boolean trimMantleInStudio = false; public int mantleKeepAlive = 30; public int cacheSize = 4_096; public int resourceLoaderCacheSize = 1_024; diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index 98a5fc7be..1a760d9d7 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -21,9 +21,12 @@ package com.volmit.iris.core; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.engine.object.IrisBiome; import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.engine.object.IrisRange; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.format.C; @@ -90,10 +93,36 @@ public class ServerConfigurator { return worlds; } - public static void installDataPacks(boolean fullInstall) { + installDataPacks(DataVersion.getDefault(), fullInstall); + } + + public static void installDataPacks(IDataFixer fixer, boolean fullInstall) { Iris.info("Checking Data Packs..."); File packs = new File("plugins/Iris/packs"); + double ultimateMaxHeight = 0; + double ultimateMinHeight = 0; + if (packs.exists() && packs.isDirectory()) { + for (File pack : packs.listFiles()) { + IrisData data = IrisData.get(pack); + if (pack.isDirectory()) { + File dimensionsFolder = new File(pack, "dimensions"); + if (dimensionsFolder.exists() && dimensionsFolder.isDirectory()) { + for (File file : dimensionsFolder.listFiles()) { + if (file.isFile() && file.getName().endsWith(".json")) { + IrisDimension dim = data.getDimensionLoader().load(file.getName().split("\\Q.\\E")[0]); + if (ultimateMaxHeight < dim.getDimensionHeight().getMax()) { + ultimateMaxHeight = dim.getDimensionHeight().getMax(); + } + if (ultimateMinHeight > dim.getDimensionHeight().getMin()) { + ultimateMinHeight = dim.getDimensionHeight().getMin(); + } + } + } + } + } + } + } if (packs.exists()) { for (File i : packs.listFiles()) { @@ -113,7 +142,7 @@ public class ServerConfigurator { Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); for (File dpack : getDatapacksFolder()) { - dim.installDataPack(() -> data, dpack); + dim.installDataPack(fixer, () -> data, dpack, ultimateMaxHeight, ultimateMinHeight); } } } diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java index 96c91305f..d8cd6a260 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java @@ -74,7 +74,7 @@ public class CommandDeepSearch implements DecreeExecutor { } DeepSearchPregenerator.DeepSearchJob DeepSearchJob = DeepSearchPregenerator.DeepSearchJob.builder() - .world(worldName) + .world(world) .radiusBlocks(radius) .position(0) .build(); 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 898b99fca..9439d29d6 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 @@ -19,10 +19,22 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.core.nms.v1X.NMSBinding1X; +import com.volmit.iris.core.pregenerator.ChunkUpdater; import com.volmit.iris.core.service.IrisEngineSVC; +import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.mantle.components.MantleObjectComponent; +import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.engine.object.IrisCave; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.engine.object.IrisEntity; +import com.volmit.iris.util.data.Dimension; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.annotations.Decree; @@ -31,18 +43,31 @@ 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.mantle.TectonicPlate; +import com.volmit.iris.util.math.Spiraler; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.nbt.mca.MCAFile; +import com.volmit.iris.util.nbt.mca.MCAUtil; import com.volmit.iris.util.plugin.VolmitSender; +import io.lumine.mythic.bukkit.adapters.BukkitEntity; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; import net.jpountz.lz4.LZ4FrameInputStream; import net.jpountz.lz4.LZ4FrameOutputStream; import org.apache.commons.lang.RandomStringUtils; import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EntityType; import java.io.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -51,36 +76,161 @@ public class CommandDeveloper implements DecreeExecutor { private CommandTurboPregen turboPregen; @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) - public void EngineStatus( - @Param(description = "World") + public void EngineStatus() { + List IrisWorlds = new ArrayList<>(); + int TotalLoadedChunks = 0; + int TotalQueuedTectonicPlates = 0; + int TotalNotQueuedTectonicPlates = 0; + int TotalTectonicPlates = 0; + + long lowestUnloadDuration = 0; + long highestUnloadDuration = 0; + + for (World world : Bukkit.getWorlds()) { + try { + if (IrisToolbelt.access(world).getEngine() != null) { + IrisWorlds.add(world); + } + } catch (Exception e) { + // no + } + } + + for (World world : IrisWorlds) { + Engine engine = IrisToolbelt.access(world).getEngine(); + TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload(); + TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions(); + TotalTectonicPlates += engine.getMantle().getLoadedRegionCount(); + if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) { + highestUnloadDuration = (long) engine.getMantle().getTectonicDuration(); + } + if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) { + lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration(); + } + for (Chunk chunk : world.getLoadedChunks()) { + if (chunk.isLoaded()) { + TotalLoadedChunks++; + } + } + } + Iris.info("-------------------------"); + Iris.info(C.DARK_PURPLE + "Engine Status"); + Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks); + Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit()); + Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates); + Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates); + Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates); + Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration)); + Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration)); + Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize())); + Iris.info("-------------------------"); + } + + @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); + c++; + Iris.info("Loaded count: " + c ); + + } + + } + + @Decree(description = "Test") + public void packBenchmark( + @Param(description = "The pack to bench", aliases = {"pack"}) + IrisDimension dimension + ) { + Iris.info("test"); + IrisPackBenchmarking benchmark = new IrisPackBenchmarking(dimension, 1); + + } + + @Decree(description = "Upgrade to another Minecraft version") + public void upgrade( + @Param(description = "The version to upgrade to", defaultValue = "latest") DataVersion version) { + sender().sendMessage(C.GREEN + "Upgrading to " + version.getVersion() + "..."); + ServerConfigurator.installDataPacks(version.get(), false); + sender().sendMessage(C.GREEN + "Done upgrading! You can now update your server version to " + version.getVersion()); + } + + @Decree(description = "Test") + public void updater( + @Param(description = "Updater for chunks") World world ) { - if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); - return; - } + Iris.info("test"); + ChunkUpdater updater = new ChunkUpdater(world); + updater.start(); - Engine engine = IrisToolbelt.access(world).getEngine(); - if(engine != null) { - long lastUseSize = engine.getMantle().getLastUseMapMemoryUsage(); - Iris.info("-------------------------"); - Iris.info(C.DARK_PURPLE + "Engine Status"); - Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit()); - Iris.info(C.DARK_PURPLE + "Tectonic Loaded Plates: " + C.LIGHT_PURPLE + engine.getMantle().getLoadedRegionCount()); - Iris.info(C.DARK_PURPLE + "Tectonic Plates: " + C.LIGHT_PURPLE + engine.getMantle().getNotClearedLoadedRegions()); - Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + engine.getMantle().getToUnload()); - Iris.info(C.DARK_PURPLE + "Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration((long) engine.getMantle().getTectonicDuration())); - Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize())); - Iris.info(C.DARK_PURPLE + "LastUse Size: " + C.LIGHT_PURPLE + Form.mem(lastUseSize)); - Iris.info("-------------------------"); - } else { - Iris.info(C.RED + "Engine is null!"); - } } - @Decree(description = "Test", origin = DecreeOrigin.BOTH) - public void test() { - Iris.info("Test Developer CMD Executed"); + + @Decree(description = "test") + public void mca ( + @Param(description = "String") String world) { + try { + File[] McaFiles = new File(world, "region").listFiles((dir, name) -> name.endsWith(".mca")); + for (File mca : McaFiles) { + MCAFile MCARegion = MCAUtil.read(mca); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Decree(description = "UnloadChunks for good reasons.") + public void unloadchunks() { + List IrisWorlds = new ArrayList<>(); + int chunksUnloaded = 0; + for (World world : Bukkit.getWorlds()) { + try { + if (IrisToolbelt.access(world).getEngine() != null) { + IrisWorlds.add(world); + } + } catch (Exception e) { + // no + } + } + + for (World world : IrisWorlds) { + for (Chunk chunk : world.getLoadedChunks()) { + if (chunk.isLoaded()) { + chunk.unload(); + chunksUnloaded++; + } + } + } + Iris.info(C.IRIS + "Chunks Unloaded: " + chunksUnloaded); + + } + + @Decree(description = "Test", aliases = {"ip"}) + public void network() { + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface ni : Collections.list(networkInterfaces)) { + Iris.info("Display Name: %s", ni.getDisplayName()); + Enumeration inetAddresses = ni.getInetAddresses(); + for (InetAddress ia : Collections.list(inetAddresses)) { + Iris.info("IP: %s", ia.getHostAddress()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } } @Decree(description = "Test the compression algorithms") 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 3601b3f11..5154ea4af 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 @@ -20,21 +20,27 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.core.pregenerator.ChunkUpdater; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisBenchmarking; import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.core.safeguard.UtilsSFG; import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.platform.BukkitChunkGenerator; import com.volmit.iris.engine.platform.DummyChunkGenerator; +import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; 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.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; import lombok.Getter; @@ -49,8 +55,10 @@ import org.bukkit.entity.Player; import org.bukkit.generator.ChunkGenerator; import org.bukkit.scheduler.BukkitRunnable; +import java.io.Console; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -59,6 +67,7 @@ import static com.volmit.iris.core.service.EditSVC.deletingWorld; import static com.volmit.iris.core.tools.IrisBenchmarking.inProgress; import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; +import static org.bukkit.Bukkit.getServer; @Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") public class CommandIris implements DecreeExecutor { @@ -71,11 +80,9 @@ public class CommandIris implements DecreeExecutor { private CommandWhat what; private CommandEdit edit; private CommandFind find; + private CommandSupport support; private CommandDeveloper developer; - - public static @Getter String BenchDimension; public static boolean worldCreation = false; - String WorldToLoad; String WorldEngine; String worldNameToCheck = "YourWorldName"; VolmitSender sender = Iris.getSender(); @@ -87,7 +94,9 @@ public class CommandIris implements DecreeExecutor { @Param(aliases = "dimension", description = "The dimension type to create the world with", defaultValue = "default") IrisDimension type, @Param(description = "The seed to generate the world with", defaultValue = "1337") - long seed + long seed, + @Param(description = "If it should convert the dimension to match the vanilla height system.", defaultValue = "false") + boolean vanillaheight ) { if(sender() instanceof Player) { if (incompatibilities.get("Multiverse-Core")) { @@ -131,6 +140,7 @@ public class CommandIris implements DecreeExecutor { .seed(seed) .sender(sender()) .studio(false) + .smartVanillaHeight(vanillaheight) .create(); } catch (Throwable e) { sender().sendMessage(C.RED + "Exception raised during creation. See the console for more details."); @@ -184,7 +194,7 @@ public class CommandIris implements DecreeExecutor { } /* - /todo Fix PREGEN + /todo @Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE) public void packbenchmark( @Param(description = "Dimension to benchmark") @@ -196,28 +206,16 @@ public class CommandIris implements DecreeExecutor { IrisPackBenchmarking.runBenchmark(); } */ - /* /todo Different approach this feels useless atm - @Decree(description = "Check for instabilities", origin = DecreeOrigin.CONSOLE) - public void fixunstable() throws InterruptedException { - if (unstablemode){ - sender().sendMessage(C.RED + "Incompatibilities are posted in console.."); - - Iris.info(C.RED + "Your server is experiencing an incompatibility with the Iris plugin."); - Iris.info(C.RED + "Please rectify this problem to avoid further complications."); - Iris.info(C.RED + "----------------------------------------------------------------"); - Iris.info(C.RED + "Command ran: /iris fixunstable"); - UtilsSFG.printIncompatibleWarnings(); - Iris.info(C.RED + "----------------------------------------------------------------"); - } else { - Iris.info(C.BLUE + "Iris is running stable.."); - sender().sendMessage("Iris is running stable.."); - } - } */ - @Decree(description = "Print world height information", origin = DecreeOrigin.PLAYER) public void height() { - sender().sendMessage(C.GREEN + "" + sender().player().getWorld().getMinHeight() + " to " + sender().player().getWorld().getMaxHeight()); - sender().sendMessage(C.GREEN + "Total Height: " + (sender().player().getWorld().getMaxHeight() - sender().player().getWorld().getMinHeight())); + if (sender().isPlayer()) { + sender().sendMessage(C.GREEN + "" + sender().player().getWorld().getMinHeight() + " to " + sender().player().getWorld().getMaxHeight()); + sender().sendMessage(C.GREEN + "Total Height: " + (sender().player().getWorld().getMaxHeight() - sender().player().getWorld().getMinHeight())); + } else { + World mainWorld = getServer().getWorlds().get(0); + Iris.info(C.GREEN + "" + mainWorld.getMinHeight() + " to " + mainWorld.getMaxHeight()); + Iris.info(C.GREEN + "Total Height: " + (mainWorld.getMaxHeight() - mainWorld.getMinHeight())); + } } @Decree(description = "QOL command to open a overworld studio world.", sync = true) @@ -226,6 +224,44 @@ public class CommandIris implements DecreeExecutor { Iris.service(StudioSVC.class).open(sender(), 1337, "overworld"); } + @Decree(description = "Check access of all worlds.", aliases = {"accesslist"}) + public void worlds() { + KList IrisWorlds = new KList<>(); + KList BukkitWorlds = new KList<>(); + + for (World w : Bukkit.getServer().getWorlds()) { + try { + Engine engine = IrisToolbelt.access(w).getEngine(); + if (engine != null) { + IrisWorlds.add(w); + } + } catch (Exception e) { + BukkitWorlds.add(w); + } + } + + if (sender().isPlayer()) { + sender().sendMessage(C.BLUE + "Iris Worlds: "); + for (World IrisWorld : IrisWorlds.copy()) { + sender().sendMessage(C.IRIS + "- " +IrisWorld.getName()); + } + sender().sendMessage(C.GOLD + "Bukkit Worlds: "); + for (World BukkitWorld : BukkitWorlds.copy()) { + sender().sendMessage(C.GRAY + "- " +BukkitWorld.getName()); + } + } else { + Iris.info(C.BLUE + "Iris Worlds: "); + for (World IrisWorld : IrisWorlds.copy()) { + Iris.info(C.IRIS + "- " +IrisWorld.getName()); + } + Iris.info(C.GOLD + "Bukkit Worlds: "); + for (World BukkitWorld : BukkitWorlds.copy()) { + Iris.info(C.GRAY + "- " +BukkitWorld.getName()); + } + + } + } + @Decree(description = "Remove an Iris world", aliases = {"del", "rm", "delete"}, sync = true) public void remove( @Param(description = "The world to remove") @@ -234,7 +270,7 @@ public class CommandIris implements DecreeExecutor { boolean delete ) { if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); return; } sender().sendMessage(C.GREEN + "Removing world: " + world.getName()); @@ -286,6 +322,24 @@ 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") @@ -417,7 +471,7 @@ public class CommandIris implements DecreeExecutor { World world ) { if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); return; } sender().sendMessage(C.GREEN + "Unloading world: " + world.getName()); @@ -445,9 +499,9 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.YELLOW + world + " Doesnt exist on the server."); return; } - WorldToLoad = world; + File BUKKIT_YML = new File("bukkit.yml"); - String pathtodim = world + "\\iris\\pack\\dimensions\\"; + String pathtodim = world + File.separator +"iris"+File.separator +"pack"+File.separator +"dimensions"+File.separator; File directory = new File(Bukkit.getWorldContainer(), pathtodim); String dimension = null; @@ -481,9 +535,10 @@ public class CommandIris implements DecreeExecutor { } catch (IOException e) { Iris.error("Failed to update bukkit.yml!"); e.printStackTrace(); + return; } } - checkForBukkitWorlds(); + checkForBukkitWorlds(world); sender().sendMessage(C.GREEN + world + " loaded successfully."); } @Decree(description = "Evacuate an iris world", origin = DecreeOrigin.PLAYER, sync = true) @@ -492,7 +547,7 @@ public class CommandIris implements DecreeExecutor { World world ) { if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); return; } sender().sendMessage(C.GREEN + "Evacuating world" + world.getName()); @@ -504,7 +559,7 @@ public class CommandIris implements DecreeExecutor { File worldDirectory = new File(worldContainer, worldName); return worldDirectory.exists() && worldDirectory.isDirectory(); } - private void checkForBukkitWorlds() { + private void checkForBukkitWorlds(String world) { FileConfiguration fc = new YamlConfiguration(); try { fc.load(new File("bukkit.yml")); @@ -513,9 +568,9 @@ public class CommandIris implements DecreeExecutor { return; } - List worldsToLoad = Collections.singletonList(WorldToLoad); + List worldsToLoad = Collections.singletonList(world); - for (String s : section.getKeys(false)) { + for (String s : section.getKeys(false)) { if (!worldsToLoad.contains(s)) { continue; } @@ -594,6 +649,6 @@ public class CommandIris implements DecreeExecutor { ff.mkdirs(); service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile()); } - return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); + return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey(), false); } } diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java index 922041241..710184027 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandJigsaw.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.edit.JigsawEditor; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.engine.framework.placer.WorldObjectPlacer; import com.volmit.iris.engine.jigsaw.PlannedStructure; import com.volmit.iris.engine.object.IrisJigsawPiece; import com.volmit.iris.engine.object.IrisJigsawStructure; @@ -34,6 +35,7 @@ import com.volmit.iris.util.decree.specialhandlers.ObjectHandler; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import java.io.File; @@ -55,9 +57,16 @@ public class CommandJigsaw implements DecreeExecutor { IrisJigsawStructure structure ) { PrecisionStopwatch p = PrecisionStopwatch.start(); - PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation()), new RNG()); - sender().sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2)); - ps.place(world()); + try { + var world = world(); + WorldObjectPlacer placer = new WorldObjectPlacer(world); + PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation().add(0, world.getMinHeight(), 0)), new RNG()); + VolmitSender sender = sender(); + sender.sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2)); + ps.place(placer, failed -> sender.sendMessage(failed ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place the structure!")); + } catch (IllegalArgumentException e) { + sender().sendMessage(C.RED + "Failed to place the structure: " + e.getMessage()); + } } @Decree(description = "Create a jigsaw piece") diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java b/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java index ea7f07f7a..830068de7 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java @@ -35,6 +35,7 @@ import com.volmit.iris.util.decree.specialhandlers.ObjectHandler; import com.volmit.iris.util.format.C; import com.volmit.iris.util.math.Direction; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.misc.E; import com.volmit.iris.util.scheduling.Queue; import org.bukkit.*; import org.bukkit.block.Block; @@ -52,7 +53,7 @@ import java.util.*; @Decree(name = "object", aliases = "o", origin = DecreeOrigin.PLAYER, studio = true, description = "Iris object manipulation") public class CommandObject implements DecreeExecutor { - private static final Set skipBlocks = Set.of(Material.GRASS, Material.SNOW, Material.VINE, Material.TORCH, Material.DEAD_BUSH, + private static final Set skipBlocks = Set.of(E.getOrDefault(Material.class, "GRASS", "SHORT_GRASS"), Material.SNOW, Material.VINE, Material.TORCH, Material.DEAD_BUSH, Material.POPPY, Material.DANDELION); public static IObjectPlacer createPlacer(World world, Map futureBlockChanges) { @@ -197,6 +198,20 @@ public class CommandObject implements DecreeExecutor { } } + @Decree(description = "Shrink an object to its minimum size") + public void shrink(@Param(description = "The object to shrink", customHandler = ObjectHandler.class) String object) { + IrisObject o = IrisData.loadAnyObject(object); + sender().sendMessage("Current Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD()); + o.shrinkwrap(); + sender().sendMessage("New Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD()); + try { + o.write(o.getLoadFile()); + } catch (IOException e) { + sender().sendMessage("Failed to save object " + o.getLoadFile() + ": " + e.getMessage()); + e.printStackTrace(); + } + } + @Decree(description = "Get a powder that reveals objects", studio = true, aliases = "d") public void dust() { player().getInventory().addItem(WandSVC.createDust()); diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java index 73f93091d..e3ed1c844 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java @@ -56,6 +56,7 @@ public class CommandPregen implements DecreeExecutor { IrisToolbelt.pregenerate(PregenTask .builder() .center(new Position2(center.getBlockX() >> 9, center.getBlockZ() >> 9)) + .gui(true) .width(w) .height(w) .build(), world); 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 621915632..774e760b8 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 @@ -26,6 +26,7 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.project.IrisProject; import com.volmit.iris.core.service.ConversionSVC; import com.volmit.iris.core.service.StudioSVC; +import com.volmit.iris.core.tools.IrisConverter; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.*; @@ -73,6 +74,7 @@ import java.nio.file.Files; import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.Date; import java.util.Objects; import java.util.concurrent.ExecutionException; @@ -84,7 +86,7 @@ import java.util.function.Supplier; public class CommandStudio implements DecreeExecutor { private CommandFind find; private CommandEdit edit; - private CommandDeepSearch deepSearch; + //private CommandDeepSearch deepSearch; public static String hrf(Duration duration) { return duration.toString().substring(2).replaceAll("(\\d[HMS])(?!$)", "$1 ").toLowerCase(); @@ -229,9 +231,9 @@ public class CommandStudio implements DecreeExecutor { @Decree(description = "Convert objects in the \"convert\" folder") public void convert() { Iris.service(ConversionSVC.class).check(sender()); + //IrisConverter.convertSchematics(sender()); } - @Decree(description = "Execute a script", aliases = "run", origin = DecreeOrigin.PLAYER) public void execute( @Param(description = "The script to run") @@ -331,6 +333,63 @@ public class CommandStudio implements DecreeExecutor { player().openInventory(inv); } + + @Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER) + public void distances(@Param(description = "The radius") int radius) { + var engine = engine(); + if (engine == null) { + sender().sendMessage(C.RED + "Only works in an Iris world!"); + return; + } + var sender = sender(); + int d = radius*2; + KMap> data = new KMap<>(); + var multiBurst = new MultiBurst("Distance Sampler", Thread.MIN_PRIORITY); + var executor = multiBurst.burst(radius * radius); + + sender.sendMessage(C.GRAY + "Generating data..."); + var loc = player().getLocation(); + new Spiraler(d, d, (x, z) -> executor.queue(() -> { + var struct = engine.getStructureAt(x, z); + if (struct != null) { + data.computeIfAbsent(struct.getLoadKey(), (k) -> new KList<>()).add(new Position2(x, z)); + } + })).setOffset(loc.getBlockX(), loc.getBlockZ()).drain(); + + executor.complete(); + multiBurst.close(); + for (var key : data.keySet()) { + var list = data.get(key); + KList distances = new KList<>(list.size() - 1); + for (int i = 0; i < list.size(); i++) { + var pos = list.get(i); + double dist = Integer.MAX_VALUE; + for (var p : list) { + if (p.equals(pos)) continue; + dist = Math.min(dist, Math.sqrt(Math.pow(pos.getX() - p.getX(), 2) + Math.pow(pos.getZ() - p.getZ(), 2))); + } + if (dist == Integer.MAX_VALUE) continue; + distances.add(Math.round(dist * 16)); + } + long[] array = new long[distances.size()]; + for (int i = 0; i < distances.size(); i++) { + array[i] = distances.get(i); + } + Arrays.sort(array); + long min = array.length > 0 ? array[0] : 0; + long max = array.length > 0 ? array[array.length - 1] : 0; + long sum = Arrays.stream(array).sum(); + long avg = array.length > 0 ? Math.round(sum / (double) array.length) : 0; + String msg = "%s: %s => min: %s/max: %s -> avg: %s".formatted(key, list.size(), min, max, avg); + sender.sendMessage(msg); + } + if (data.isEmpty()) { + sender.sendMessage(C.RED + "No data found!"); + } else { + sender.sendMessage(C.GREEN + "Done!"); + } + } + @Decree(description = "Render a world map (External GUI)", aliases = "render") public void map( @Param(name = "world", description = "The world to open the generator for", contextual = true) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java b/core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java new file mode 100644 index 000000000..0f7baad0d --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java @@ -0,0 +1,82 @@ +/* + * 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.core.commands; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.pregenerator.ChunkUpdater; +import com.volmit.iris.core.service.IrisEngineSVC; +import com.volmit.iris.core.tools.IrisPackBenchmarking; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.decree.DecreeExecutor; +import com.volmit.iris.util.decree.DecreeOrigin; +import com.volmit.iris.util.decree.annotations.Decree; +import com.volmit.iris.util.decree.annotations.Param; +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.mantle.TectonicPlate; +import com.volmit.iris.util.misc.Hastebin; +import com.volmit.iris.util.misc.Platform; +import com.volmit.iris.util.misc.getHardware; +import com.volmit.iris.util.nbt.mca.MCAFile; +import com.volmit.iris.util.nbt.mca.MCAUtil; +import com.volmit.iris.util.plugin.VolmitSender; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import net.jpountz.lz4.LZ4FrameInputStream; +import net.jpountz.lz4.LZ4FrameOutputStream; +import org.apache.commons.lang.RandomStringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import oshi.SystemInfo; + +import java.io.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +@Decree(name = "Support", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"support"}) +public class CommandSupport implements DecreeExecutor { + + @Decree(description = "report") + public void report() { + try { + if (sender().isPlayer()) sender().sendMessage(C.GOLD + "Creating report.."); + if (!sender().isPlayer()) Iris.info(C.GOLD + "Creating report.."); + Hastebin.enviornment(sender()); + + } catch (Exception e) { + Iris.info(C.RED + "Something went wrong: "); + e.printStackTrace(); + } + } + + +} + + 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 new file mode 100644 index 000000000..cb1ff6eeb --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java @@ -0,0 +1,107 @@ +/* + * 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.core.commands; + +import org.bukkit.World; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.pregenerator.ChunkUpdater; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.util.decree.DecreeExecutor; +import com.volmit.iris.util.decree.DecreeOrigin; +import com.volmit.iris.util.decree.annotations.Decree; +import com.volmit.iris.util.decree.annotations.Param; +import com.volmit.iris.util.format.C; +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; + + @Decree(description = "Updates all chunk in the specified world") + public void start( + @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 = 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(); + } + + @Decree(description = "Pause the updater") + public void pause( + @Param(description = "World to pause the Updater at") + World world + ) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.GOLD + "This is not an Iris world"); + return; + } + if (chunkUpdater == null) { + sender().sendMessage(C.GOLD + "You cant pause something that doesnt exist?"); + return; + } + boolean status = chunkUpdater.pause(); + if (sender().isPlayer()) { + if (status) { + sender().sendMessage(C.IRIS + "Paused task for: " + C.GRAY + world.getName()); + } else { + sender().sendMessage(C.IRIS + "Unpause task for: " + C.GRAY + world.getName()); + } + } else { + if (status) { + Iris.info(C.IRIS + "Paused task for: " + C.GRAY + world.getName()); + } else { + Iris.info(C.IRIS + "Unpause task for: " + C.GRAY + world.getName()); + } + } + } + + @Decree(description = "Stops the updater") + public void stop( + @Param(description = "World to stop the Updater at") + World world + ) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.GOLD + "This is not an Iris world"); + return; + } + if (chunkUpdater == null) { + sender().sendMessage(C.GOLD + "You cant stop something that doesnt exist?"); + return; + } + if (sender().isPlayer()) { + sender().sendMessage("Stopping Updater for: " + C.GRAY + world.getName()); + } else { + Iris.info("Stopping Updater for: " + C.GRAY + world.getName()); + } + chunkUpdater.stop(); + } + +} + + diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandWhat.java b/core/src/main/java/com/volmit/iris/core/commands/CommandWhat.java index 3686ad81a..48d8c726e 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandWhat.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandWhat.java @@ -22,7 +22,9 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.edit.BlockSignal; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.engine.object.IrisRegion; import com.volmit.iris.util.data.B; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; @@ -37,6 +39,7 @@ import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; @Decree(name = "what", origin = DecreeOrigin.PLAYER, studio = true, description = "Iris What?") @@ -82,6 +85,19 @@ public class CommandWhat implements DecreeExecutor { } } + @Decree(description = "What region am i in?", origin = DecreeOrigin.PLAYER) + public void region() { + try { + Chunk chunk = world().getChunkAt(player().getLocation().getBlockZ() / 16, player().getLocation().getBlockZ() / 16); + IrisRegion r = engine().getRegion(chunk); + sender().sendMessage("IRegion: " + r.getLoadKey() + " (" + r.getName() + ")"); + + } catch (Throwable e) { + Iris.reportError(e); + sender().sendMessage(C.IRIS + "Iris worlds only."); + } + } + @Decree(description = "What block am i looking at?", origin = DecreeOrigin.PLAYER) public void block() { BlockData bd; @@ -147,7 +163,7 @@ public class CommandWhat implements DecreeExecutor { sender().sendMessage("Found " + v.get() + " Nearby Markers (" + marker + ")"); } else { - sender().sendMessage("Iris worlds only."); + sender().sendMessage(C.IRIS + "Iris worlds only."); } } } diff --git a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java index 25f3ee710..6d3534971 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java +++ b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java @@ -24,6 +24,7 @@ import com.volmit.iris.core.pregenerator.IrisPregenerator; import com.volmit.iris.core.pregenerator.PregenListener; import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.pregenerator.PregeneratorMethod; +import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.format.Form; @@ -44,6 +45,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; +import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmarkInProgress; + public class PregeneratorJob implements PregenListener { private static final Color COLOR_EXISTS = parseColor("#4d7d5b"); private static final Color COLOR_BLACK = parseColor("#4d7d5b"); @@ -86,7 +89,7 @@ public class PregeneratorJob implements PregenListener { max.setZ(Math.max((zz << 5) + 31, max.getZ())); }); - if (IrisSettings.get().getGui().isUseServerLaunchedGuis()) { + if (IrisSettings.get().getGui().isUseServerLaunchedGuis() && task.isGui()) { open(); } diff --git a/core/src/main/java/com/volmit/iris/core/gui/components/IrisRenderer.java b/core/src/main/java/com/volmit/iris/core/gui/components/IrisRenderer.java index 2820b8999..a96432427 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/components/IrisRenderer.java +++ b/core/src/main/java/com/volmit/iris/core/gui/components/IrisRenderer.java @@ -55,10 +55,10 @@ public class IrisRenderer { IrisBiome b = renderer.getBiome((int) Math.round(x), renderer.getMaxHeight() - 1, (int) Math.round(z)); IrisBiomeGeneratorLink g = b.getGenerators().get(0); Color c; - if (g.getMax() <= 0) { + if (g.getMax(renderer) <= 0) { // Max is below water level, so it is most likely an ocean biome c = Color.BLUE; - } else if (g.getMin() < 0) { + } else if (g.getMin(renderer) < 0) { // Min is below water level, but max is not, so it is most likely a shore biome c = Color.YELLOW; } else { diff --git a/core/src/main/java/com/volmit/iris/core/link/EcoItemsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/EcoItemsDataProvider.java new file mode 100644 index 000000000..615256e66 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/link/EcoItemsDataProvider.java @@ -0,0 +1,71 @@ +package com.volmit.iris.core.link; + +import com.volmit.iris.Iris; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.reflect.WrappedField; +import com.willfp.ecoitems.items.EcoItem; +import com.willfp.ecoitems.items.EcoItems; +import org.bukkit.NamespacedKey; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.ItemStack; + +import java.util.MissingResourceException; + +public class EcoItemsDataProvider extends ExternalDataProvider { + private WrappedField itemStack; + private WrappedField id; + + public EcoItemsDataProvider() { + super("EcoItems"); + } + + @Override + public void init() { + Iris.info("Setting up EcoItems Link..."); + itemStack = new WrappedField<>(EcoItem.class, "_itemStack"); + if (this.itemStack.hasFailed()) { + Iris.error("Failed to set up EcoItems Link: Unable to fetch ItemStack field!"); + } + id = new WrappedField<>(EcoItem.class, "id"); + if (this.id.hasFailed()) { + Iris.error("Failed to set up EcoItems Link: Unable to fetch id field!"); + } + } + + @Override + public BlockData getBlockData(Identifier blockId) throws MissingResourceException { + throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + } + + @Override + public ItemStack getItemStack(Identifier itemId) throws MissingResourceException { + EcoItem item = EcoItems.INSTANCE.getByID(itemId.key()); + if (item == null) throw new MissingResourceException("Failed to find Item!", itemId.namespace(), itemId.key()); + return itemStack.get(item).clone(); + } + + @Override + public Identifier[] getBlockTypes() { + return new Identifier[0]; + } + + @Override + public Identifier[] getItemTypes() { + KList names = new KList<>(); + for (EcoItem item : EcoItems.INSTANCE.values()) { + try { + Identifier key = Identifier.fromNamespacedKey(id.get(item)); + if (getItemStack(key) != null) + names.add(key); + } catch (MissingResourceException ignored) { + } + } + + return names.toArray(new Identifier[0]); + } + + @Override + public boolean isValidProvider(Identifier id, boolean isItem) { + return id.namespace().equalsIgnoreCase("ecoitems") && isItem; + } +} 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 ee4d46bfc..7e3b4d00e 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,8 +1,10 @@ package com.volmit.iris.core.link; +import com.volmit.iris.engine.framework.Engine; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit; +import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; @@ -28,6 +30,7 @@ public abstract class ExternalDataProvider { public abstract BlockData getBlockData(Identifier blockId) throws MissingResourceException; public abstract ItemStack getItemStack(Identifier itemId) throws MissingResourceException; + public void processUpdate(Engine engine, Block block, Identifier blockId) {}; public abstract Identifier[] getBlockTypes(); diff --git a/core/src/main/java/com/volmit/iris/core/link/HMCLeavesDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/HMCLeavesDataProvider.java new file mode 100644 index 000000000..44c695a28 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/link/HMCLeavesDataProvider.java @@ -0,0 +1,135 @@ +package com.volmit.iris.core.link; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.data.IrisBlockData; +import com.volmit.iris.util.reflect.WrappedField; +import com.volmit.iris.util.reflect.WrappedReturningMethod; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Leaves; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.MissingResourceException; +import java.util.function.Supplier; + +public class HMCLeavesDataProvider extends ExternalDataProvider { + private Object apiInstance; + private WrappedReturningMethod worldBlockType; + private WrappedReturningMethod setCustomBlock; + private Map blockDataMap = Map.of(); + private Map> itemDataField = Map.of(); + + public HMCLeavesDataProvider() { + super("HMCLeaves"); + } + + @Override + public String getPluginId() { + return "HMCLeaves"; + } + + @Override + public void init() { + try { + worldBlockType = new WrappedReturningMethod<>((Class) Class.forName("io.github.fisher2911.hmcleaves.data.BlockData"), "worldBlockType"); + apiInstance = getApiInstance(Class.forName("io.github.fisher2911.hmcleaves.api.HMCLeavesAPI")); + setCustomBlock = new WrappedReturningMethod<>((Class) apiInstance.getClass(), "setCustomBlock", Location.class, String.class, boolean.class); + Object config = getLeavesConfig(apiInstance.getClass()); + blockDataMap = getMap(config, "blockDataMap"); + itemDataField = getMap(config, "itemSupplierMap"); + } catch (Throwable e) { + Iris.error("Failed to initialize HMCLeavesDataProvider: " + e.getMessage()); + } + } + + @Override + public BlockData getBlockData(Identifier blockId) throws MissingResourceException { + Object o = blockDataMap.get(blockId.key()); + if (o == null) + throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + Material material = worldBlockType.invoke(o, new Object[0]); + if (material == null) + throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + BlockData blockData = Bukkit.createBlockData(material); + if (IrisSettings.get().getGenerator().preventLeafDecay && blockData instanceof Leaves leaves) + leaves.setPersistent(true); + return new IrisBlockData(blockData, blockId); + } + + @Override + public ItemStack getItemStack(Identifier itemId) throws MissingResourceException { + if (!itemDataField.containsKey(itemId.key())) + throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + return itemDataField.get(itemId.key()).get(); + } + + @Override + public void processUpdate(Engine engine, Block block, Identifier blockId) { + Boolean result = setCustomBlock.invoke(apiInstance, new Object[]{block.getLocation(), blockId.key(), false}); + if (result == null || !result) + Iris.warn("Failed to set custom block! " + blockId.key() + " " + block.getX() + " " + block.getY() + " " + block.getZ()); + else if (IrisSettings.get().getGenerator().preventLeafDecay) { + BlockData blockData = block.getBlockData(); + if (blockData instanceof Leaves leaves) + leaves.setPersistent(true); + } + } + + @Override + public Identifier[] getBlockTypes() { + KList names = new KList<>(); + for (String name : blockDataMap.keySet()) { + try { + Identifier key = new Identifier("hmcleaves", name); + if (getBlockData(key) != null) + names.add(key); + } catch (MissingResourceException ignored) { + } + } + + return names.toArray(new Identifier[0]); + } + + @Override + public Identifier[] getItemTypes() { + KList names = new KList<>(); + for (String name : itemDataField.keySet()) { + try { + Identifier key = new Identifier("hmcleaves", name); + if (getItemStack(key) != null) + names.add(key); + } catch (MissingResourceException ignored) { + } + } + + return names.toArray(new Identifier[0]); + } + + @Override + public boolean isValidProvider(Identifier id, boolean isItem) { + return (isItem ? itemDataField.keySet() : blockDataMap.keySet()).contains(id.key()); + } + + private Map getMap(C config, String name) { + WrappedField> field = new WrappedField<>((Class) config.getClass(), name); + return field.get(config); + } + + private A getApiInstance(Class apiClass) { + WrappedReturningMethod instance = new WrappedReturningMethod<>(apiClass, "getInstance"); + return instance.invoke(); + } + + private C getLeavesConfig(Class apiClass) { + WrappedReturningMethod instance = new WrappedReturningMethod<>(apiClass, "getInstance"); + WrappedField config = new WrappedField<>(apiClass, "config"); + return config.get(instance.invoke()); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/link/Identifier.java b/core/src/main/java/com/volmit/iris/core/link/Identifier.java index 4059016d0..3969b174b 100644 --- a/core/src/main/java/com/volmit/iris/core/link/Identifier.java +++ b/core/src/main/java/com/volmit/iris/core/link/Identifier.java @@ -6,6 +6,10 @@ public record Identifier(String namespace, String key) { private static final String DEFAULT_NAMESPACE = "minecraft"; + public static Identifier fromNamespacedKey(NamespacedKey key) { + return new Identifier(key.getNamespace(), key.getKey()); + } + public static Identifier fromString(String id) { String[] strings = id.split(":", 2); if (strings.length == 1) { diff --git a/core/src/main/java/com/volmit/iris/core/link/MMOItemsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/MMOItemsDataProvider.java new file mode 100644 index 000000000..4445f3e9d --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/link/MMOItemsDataProvider.java @@ -0,0 +1,106 @@ +package com.volmit.iris.core.link; + +import com.volmit.iris.Iris; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.scheduling.J; +import net.Indyuce.mmoitems.MMOItems; +import net.Indyuce.mmoitems.api.Type; +import net.Indyuce.mmoitems.api.block.CustomBlock; +import org.bukkit.Bukkit; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.ItemStack; + +import java.util.MissingResourceException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class MMOItemsDataProvider extends ExternalDataProvider { + + public MMOItemsDataProvider() { + super("MMOItems"); + } + + @Override + public void init() { + Iris.info("Setting up MMOItems Link..."); + } + + @Override + public BlockData getBlockData(Identifier blockId) throws MissingResourceException { + int id = -1; + try { + id = Integer.parseInt(blockId.key()); + } catch (NumberFormatException ignored) {} + CustomBlock block = api().getCustomBlocks().getBlock(id); + if (block == null) throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + return block.getState().getBlockData(); + } + + @Override + public ItemStack getItemStack(Identifier itemId) throws MissingResourceException { + String[] parts = itemId.namespace().split("_", 2); + if (parts.length != 2) + throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + CompletableFuture future = new CompletableFuture<>(); + Runnable run = () -> future.complete(api().getItem(parts[1], itemId.key())); + if (Bukkit.isPrimaryThread()) run.run(); + else J.s(run); + ItemStack item = null; + try { + item = future.get(); + } catch (InterruptedException | ExecutionException ignored) {} + if (item == null) + throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + return item; + } + + @Override + public Identifier[] getBlockTypes() { + KList names = new KList<>(); + for (Integer id : api().getCustomBlocks().getBlockIds()) { + try { + Identifier key = new Identifier("mmoitems", String.valueOf(id)); + if (getBlockData(key) != null) + names.add(key); + } catch (MissingResourceException ignored) { + } + } + return names.toArray(new Identifier[0]); + } + + @Override + public Identifier[] getItemTypes() { + KList names = new KList<>(); + Runnable run = () -> { + for (Type type : api().getTypes().getAll()) { + for (String name : api().getTemplates().getTemplateNames(type)) { + try { + Identifier key = new Identifier("mmoitems_" + type.getId(), name); + if (getItemStack(key) != null) + names.add(key); + } catch (MissingResourceException ignored) { + } + } + } + }; + if (Bukkit.isPrimaryThread()) run.run(); + else { + try { + J.sfut(run).get(); + } catch (InterruptedException | ExecutionException e) { + Iris.error("Failed getting MMOItems item types!"); + Iris.reportError(e); + } + } + return names.toArray(new Identifier[0]); + } + + @Override + public boolean isValidProvider(Identifier id, boolean isItem) { + return isItem ? id.namespace().split("_", 2).length == 2 : id.namespace().equals("mmoitems"); + } + + private MMOItems api() { + return MMOItems.plugin; + } +} diff --git a/core/src/main/java/com/volmit/iris/core/link/OraxenDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/OraxenDataProvider.java index 068c8c18e..58e21883d 100644 --- a/core/src/main/java/com/volmit/iris/core/link/OraxenDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/OraxenDataProvider.java @@ -19,18 +19,25 @@ package com.volmit.iris.core.link; import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.data.B; +import com.volmit.iris.util.data.IrisBlockData; import com.volmit.iris.util.reflect.WrappedField; import io.th0rgal.oraxen.api.OraxenItems; import io.th0rgal.oraxen.items.ItemBuilder; +import io.th0rgal.oraxen.mechanics.Mechanic; import io.th0rgal.oraxen.mechanics.MechanicFactory; import io.th0rgal.oraxen.mechanics.MechanicsManager; import io.th0rgal.oraxen.mechanics.provided.gameplay.block.BlockMechanic; import io.th0rgal.oraxen.mechanics.provided.gameplay.block.BlockMechanicFactory; +import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureFactory; +import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; import io.th0rgal.oraxen.mechanics.provided.gameplay.noteblock.NoteBlockMechanicFactory; import io.th0rgal.oraxen.mechanics.provided.gameplay.stringblock.StringBlockMechanicFactory; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.MultipleFacing; import org.bukkit.inventory.ItemStack; @@ -69,6 +76,8 @@ public class OraxenDataProvider extends ExternalDataProvider { return newBlockData; } else if (factory instanceof StringBlockMechanicFactory f) { return f.createTripwireData(blockId.key()); + } else if (factory instanceof FurnitureFactory) { + return new IrisBlockData(B.getAir(), blockId); } else throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); } @@ -79,6 +88,14 @@ public class OraxenDataProvider extends ExternalDataProvider { return opt.orElseThrow(() -> new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key())).build(); } + @Override + public void processUpdate(Engine engine, Block block, Identifier blockId) { + Mechanic mechanic = getFactory(blockId).getMechanic(blockId.key()); + if (mechanic instanceof FurnitureMechanic f) { + f.place(block.getLocation()); + } + } + @Override public Identifier[] getBlockTypes() { KList names = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMS.java b/core/src/main/java/com/volmit/iris/core/nms/INMS.java index dcb5e8dce..85b3778e9 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMS.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMS.java @@ -23,7 +23,13 @@ import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.nms.v1X.NMSBinding1X; import org.bukkit.Bukkit; +import java.util.Map; + public class INMS { + private static final Map REVISION = Map.of( + "1.20.5", "v1_20_R4", + "1.20.6", "v1_20_R4" + ); //@done private static final INMSBinding binding = bind(); @@ -37,7 +43,12 @@ public class INMS { } try { - return Bukkit.getServer().getClass().getCanonicalName().split("\\Q.\\E")[3]; + String name = Bukkit.getServer().getClass().getCanonicalName(); + if (name.equals("org.bukkit.craftbukkit.CraftServer")) { + return REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0], "BUKKIT"); + } else { + return name.split("\\Q.\\E")[3]; + } } catch (Throwable e) { Iris.reportError(e); Iris.error("Failed to determine server nms version!"); 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 270a04504..da90e6fb2 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 @@ -18,10 +18,12 @@ package com.volmit.iris.core.nms; +import com.volmit.iris.core.nms.datapack.DataVersion; 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.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer; import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess; import com.volmit.iris.util.nbt.tag.CompoundTag; @@ -32,6 +34,8 @@ import org.bukkit.WorldCreator; import org.bukkit.block.Biome; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; @@ -103,4 +107,12 @@ public interface INMSBinding { void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos); void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException; + + Vector3d getBoundingbox(org.bukkit.entity.EntityType entity); + + Entity spawnEntity(Location location, EntityType type, CreatureSpawnEvent.SpawnReason reason); + + default DataVersion getDataVersion() { + return DataVersion.V1192; + } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java new file mode 100644 index 000000000..7185b693d --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java @@ -0,0 +1,40 @@ +package com.volmit.iris.core.nms.datapack; + +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.nms.datapack.v1192.DataFixerV1192; +import com.volmit.iris.core.nms.datapack.v1206.DataFixerV1206; +import com.volmit.iris.util.collection.KMap; +import lombok.AccessLevel; +import lombok.Getter; + +import java.util.function.Supplier; + +//https://minecraft.wiki/w/Pack_format +@Getter +public enum DataVersion { + V1192("1.19.2", 10, DataFixerV1192::new), + V1205("1.20.6", 41, DataFixerV1206::new); + private static final KMap cache = new KMap<>(); + @Getter(AccessLevel.NONE) + private final Supplier constructor; + private final String version; + private final int packFormat; + + DataVersion(String version, int packFormat, Supplier constructor) { + this.constructor = constructor; + this.packFormat = packFormat; + this.version = version; + } + + public IDataFixer get() { + return cache.computeIfAbsent(this, k -> constructor.get()); + } + + public static IDataFixer getDefault() { + return INMS.get().getDataVersion().get(); + } + + public static DataVersion getLatest() { + return values()[values().length - 1]; + } +} diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java new file mode 100644 index 000000000..76a30f6e0 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java @@ -0,0 +1,11 @@ +package com.volmit.iris.core.nms.datapack; + +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.util.json.JSONObject; + +public interface IDataFixer { + + JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json); + + JSONObject fixDimension(JSONObject json); +} diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java new file mode 100644 index 000000000..c6bd59359 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java @@ -0,0 +1,18 @@ +package com.volmit.iris.core.nms.datapack.v1192; + +import com.volmit.iris.core.nms.datapack.IDataFixer; +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.util.json.JSONObject; + +public class DataFixerV1192 implements IDataFixer { + + @Override + public JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) { + return json; + } + + @Override + public JSONObject fixDimension(JSONObject json) { + return json; + } +} diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java new file mode 100644 index 000000000..48883bfa5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java @@ -0,0 +1,54 @@ +package com.volmit.iris.core.nms.datapack.v1206; + +import com.volmit.iris.core.nms.datapack.IDataFixer; +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.engine.object.IrisBiomeCustomSpawn; +import com.volmit.iris.engine.object.IrisBiomeCustomSpawnType; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.json.JSONArray; +import com.volmit.iris.util.json.JSONObject; + +import java.util.Locale; + +public class DataFixerV1206 implements IDataFixer { + @Override + public JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) { + int spawnRarity = biome.getSpawnRarity(); + if (spawnRarity > 0) { + json.put("creature_spawn_probability", Math.min(spawnRarity/20d, 0.9999999)); + } + + var spawns = biome.getSpawns(); + if (spawns != null && spawns.isNotEmpty()) { + JSONObject spawners = new JSONObject(); + KMap groups = new KMap<>(); + + for (IrisBiomeCustomSpawn i : spawns) { + JSONArray g = groups.computeIfAbsent(i.getGroup(), (k) -> new JSONArray()); + JSONObject o = new JSONObject(); + o.put("type", "minecraft:" + i.getType().name().toLowerCase()); + o.put("weight", i.getWeight()); + o.put("minCount", Math.min(i.getMinCount()/20d, 0)); + o.put("maxCount", Math.min(i.getMaxCount()/20d, 0.9999999)); + g.put(o); + } + + for (IrisBiomeCustomSpawnType i : groups.k()) { + spawners.put(i.name().toLowerCase(Locale.ROOT), groups.get(i)); + } + + json.put("spawners", spawners); + } + return json; + } + + @Override + public JSONObject fixDimension(JSONObject json) { + if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel)) + return json; + var value = (JSONObject) lightLevel.remove("value"); + lightLevel.put("max_inclusive", value.get("max_inclusive")); + lightLevel.put("min_inclusive", value.get("min_inclusive")); + return json; + } +} 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 04c95b4ed..e99478753 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 @@ -25,6 +25,7 @@ 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.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer; import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess; import com.volmit.iris.util.nbt.tag.CompoundTag; @@ -34,6 +35,8 @@ import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; @@ -85,6 +88,15 @@ public class NMSBinding1X implements INMSBinding { } + public Vector3d getBoundingbox() { + return null; + } + + @Override + public Entity spawnEntity(Location location, EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return location.getWorld().spawnEntity(location, type); + } + @Override public void deserializeTile(CompoundTag s, Location newPosition) { @@ -203,6 +215,11 @@ public class NMSBinding1X implements INMSBinding { } + @Override + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + return null; + } + @Override public MCAPaletteAccess createPalette() { Iris.error("Cannot use the global data palette! Iris is incapable of using MCA generation on this version of minecraft!"); 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 new file mode 100644 index 000000000..d89ad3340 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java @@ -0,0 +1,318 @@ +package com.volmit.iris.core.pregenerator; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.tools.IrisToolbelt; +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.format.Form; +import com.volmit.iris.util.math.M; +import com.volmit.iris.util.math.RollingSequence; +import com.volmit.iris.util.math.Spiraler; +import com.volmit.iris.util.scheduling.J; +import io.papermc.lib.PaperLib; +import org.bukkit.Chunk; +import org.bukkit.World; + +import java.io.File; + +import java.util.ArrayList; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +public class ChunkUpdater { + private AtomicBoolean paused; + private AtomicBoolean cancelled; + private KMap lastUse; + private final RollingSequence chunksPerSecond; + private final AtomicInteger worldheightsize; + private final AtomicInteger worldwidthsize; + private final AtomicInteger totalChunks; + private final AtomicInteger totalMaxChunks; + private final AtomicInteger totalMcaregions; + private final AtomicInteger position; + private AtomicInteger chunksProcessed; + private AtomicInteger chunksUpdated; + private AtomicLong startTime; + private ExecutorService executor; + private ExecutorService chunkExecutor; + private ScheduledExecutorService scheduler; + private CompletableFuture future; + private CountDownLatch latch; + private final Object pauseLock; + private final Engine engine; + private final World world; + + public ChunkUpdater(World world) { + this.engine = IrisToolbelt.access(world).getEngine(); + this.chunksPerSecond = new RollingSequence(5); + this.world = world; + this.lastUse = new KMap(); + this.worldheightsize = new AtomicInteger(calculateWorldDimensions(new File(world.getWorldFolder(), "region"), 1)); + this.worldwidthsize = new AtomicInteger(calculateWorldDimensions(new File(world.getWorldFolder(), "region"), 0)); + int m = Math.max(worldheightsize.get(), worldwidthsize.get()); + this.executor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors() / 3, 1)); + this.chunkExecutor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors() / 3, 1)); + this.scheduler = Executors.newScheduledThreadPool(1); + this.future = new CompletableFuture<>(); + this.startTime = new AtomicLong(); + this.worldheightsize.set(m); + this.worldwidthsize.set(m); + this.totalMaxChunks = new AtomicInteger((worldheightsize.get() / 16) * (worldwidthsize.get() / 16)); + this.chunksProcessed = new AtomicInteger(); + this.chunksUpdated = new AtomicInteger(); + this.position = new AtomicInteger(0); + this.latch = new CountDownLatch(totalMaxChunks.get()); + this.paused = new AtomicBoolean(false); + this.pauseLock = new Object(); + this.cancelled = new AtomicBoolean(false); + this.totalChunks = new AtomicInteger(0); + this.totalMcaregions = new AtomicInteger(0); + } + + public int getChunks() { + return totalMaxChunks.get(); + } + + public void start() { + unloadAndSaveAllChunks(); + update(); + } + + public boolean pause() { + unloadAndSaveAllChunks(); + if (paused.get()) { + paused.set(false); + return false; + } else { + paused.set(true); + return true; + } + } + + public void stop() { + unloadAndSaveAllChunks(); + cancelled.set(true); + } + + + private void update() { + Iris.info("Updating.."); + try { + startTime.set(System.currentTimeMillis()); + scheduler.scheduleAtFixedRate(() -> { + try { + if (!paused.get()) { + long eta = computeETA(); + long elapsedSeconds = (System.currentTimeMillis() - startTime.get()) / 1000; + int processed = chunksProcessed.get(); + double cps = elapsedSeconds > 0 ? processed / (double) elapsedSeconds : 0; + chunksPerSecond.put(cps); + double percentage = ((double) chunksProcessed.get() / (double) totalMaxChunks.get()) * 100; + if (!cancelled.get()) { + Iris.info("Updated: " + Form.f(processed) + " of " + Form.f(totalMaxChunks.get()) + " (%.0f%%) " + Form.f(chunksPerSecond.getAverage()) + "/s, ETA: " + Form.duration(eta, + 2), percentage); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + }, 0, 3, TimeUnit.SECONDS); + + CompletableFuture.runAsync(() -> { + for (int i = 0; i < totalMaxChunks.get(); i++) { + if (paused.get()) { + synchronized (pauseLock) { + try { + pauseLock.wait(); + } catch (InterruptedException e) { + Iris.error("Interrupted while waiting for executor: "); + e.printStackTrace(); + break; + } + } + } + executor.submit(() -> { + if (!cancelled.get()) { + processNextChunk(); + } + latch.countDown(); + }); + } + }).thenRun(() -> { + try { + latch.await(); + close(); + } catch (Exception e) { + Thread.currentThread().interrupt(); + } + }); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void close() { + try { + unloadAndSaveAllChunks(); + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); + chunkExecutor.shutdown(); + chunkExecutor.awaitTermination(5, TimeUnit.SECONDS); + scheduler.shutdownNow(); + } catch (Exception ignored) { + } + if (cancelled.get()) { + Iris.info("Updated: " + Form.f(chunksUpdated.get()) + " Chunks"); + Iris.info("Irritated: " + Form.f(chunksProcessed.get()) + " of " + Form.f(totalMaxChunks.get())); + Iris.info("Stopped updater."); + } else { + Iris.info("Processed: " + Form.f(chunksProcessed.get()) + " Chunks"); + Iris.info("Finished Updating: " + Form.f(chunksUpdated.get()) + " Chunks"); + } + } + + private void processNextChunk() { + int pos = position.getAndIncrement(); + int[] coords = getChunk(pos); + if (loadChunksIfGenerated(coords[0], coords[1])) { + Chunk c = world.getChunkAt(coords[0], coords[1]); + engine.updateChunk(c); + chunksUpdated.incrementAndGet(); + } + chunksProcessed.getAndIncrement(); + } + + private boolean loadChunksIfGenerated(int x, int z) { + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + if (!PaperLib.isChunkGenerated(world, x + dx, z + dz)) { + return false; + } + } + } + + AtomicBoolean generated = new AtomicBoolean(true); + KList> futures = new KList<>(9); + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + int xx = x + dx; + int zz = z + dz; + futures.add(chunkExecutor.submit(() -> { + Chunk c; + try { + c = PaperLib.getChunkAtAsync(world, xx, zz, false).get(); + } catch (InterruptedException | ExecutionException e) { + generated.set(false); + return; + } + if (!c.isLoaded()) { + CountDownLatch latch = new CountDownLatch(1); + J.s(() -> { + c.load(false); + latch.countDown(); + }); + try { + latch.await(); + } catch (InterruptedException ignored) {} + } + if (!c.isGenerated()) { + generated.set(false); + } + lastUse.put(c, M.ms()); + })); + } + } + while (!futures.isEmpty()) { + futures.removeIf(Future::isDone); + try { + Thread.sleep(50); + } catch (InterruptedException ignored) {} + } + return generated.get(); + } + + private void unloadAndSaveAllChunks() { + try { + J.sfut(() -> { + if (world == null) { + Iris.warn("World was null somehow..."); + return; + } + + for (Chunk i : new ArrayList<>(lastUse.keySet())) { + Long lastUseTime = lastUse.get(i); + if (lastUseTime != null && M.ms() - lastUseTime >= 5000) { + i.unload(); + lastUse.remove(i); + } + } + world.save(); + }).get(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private long computeETA() { + return (long) (totalMaxChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total? + // If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers) + ((totalMaxChunks.get() - chunksProcessed.get()) * ((double) (M.ms() - startTime.get()) / (double) chunksProcessed.get())) : + // If no, use quick function (which is less accurate over time but responds better to the initial delay) + ((totalMaxChunks.get() - chunksProcessed.get()) / chunksPerSecond.getAverage()) * 1000 + ); + } + + public int calculateWorldDimensions(File regionDir, Integer o) { + File[] files = regionDir.listFiles((dir, name) -> name.endsWith(".mca")); + + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minZ = Integer.MAX_VALUE; + int maxZ = Integer.MIN_VALUE; + + for (File file : files) { + String[] parts = file.getName().split("\\."); + int x = Integer.parseInt(parts[1]); + int z = Integer.parseInt(parts[2]); + + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (z < minZ) minZ = z; + if (z > maxZ) maxZ = z; + } + + int height = (maxX - minX + 1) * 32 * 16; + int width = (maxZ - minZ + 1) * 32 * 16; + + if (o == 1) { + return height; + } + if (o == 0) { + return width; + } + return 0; + } + + public int[] getChunk(int position) { + int p = -1; + AtomicInteger xx = new AtomicInteger(); + AtomicInteger zz = new AtomicInteger(); + Spiraler s = new Spiraler(worldheightsize.get() * 2, worldwidthsize.get() * 2, (x, z) -> { + xx.set(x); + zz.set(z); + }); + + while (s.hasNext() && p++ < position) { + s.next(); + } + int[] coords = new int[2]; + coords[0] = xx.get(); + coords[1] = zz.get(); + + return coords; + } +} diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/DeepSearchPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/DeepSearchPregenerator.java index c0a20534c..c60724d2a 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/DeepSearchPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/DeepSearchPregenerator.java @@ -5,18 +5,16 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.util.collection.KList; 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.mantle.MantleFlag; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RollingSequence; import com.volmit.iris.util.math.Spiraler; import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; -import com.volmit.iris.util.scheduling.PrecisionStopwatch; -import io.papermc.lib.PaperLib; import lombok.Builder; import lombok.Data; import lombok.Getter; @@ -33,7 +31,6 @@ import java.io.IOException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @@ -56,10 +53,12 @@ public class DeepSearchPregenerator extends Thread implements Listener { private final RollingSequence chunksPerMinute; private final AtomicInteger chunkCachePos; private final AtomicInteger chunkCacheSize; + private int pos; private final AtomicInteger foundCacheLast; private final AtomicInteger foundCache; private LinkedHashMap chunkCache; - private final ReentrantLock cacheLock = new ReentrantLock(); + private KList chunkQueue; + private final ReentrantLock cacheLock; private static final Map jobs = new HashMap<>(); @@ -69,11 +68,13 @@ public class DeepSearchPregenerator extends Thread implements Listener { this.chunkCachePos = new AtomicInteger(1000); this.foundCacheLast = new AtomicInteger(); this.foundCache = new AtomicInteger(); + this.cacheLock = new ReentrantLock(); this.destination = destination; - this.chunkCache = new LinkedHashMap(); + this.chunkCache = new LinkedHashMap<>(); this.maxPosition = new Spiraler(job.getRadiusBlocks() * 2, job.getRadiusBlocks() * 2, (x, z) -> { }).count(); - this.world = Bukkit.getWorld(job.getWorld()); + this.world = Bukkit.getWorld(job.getWorld().getUID()); + this.chunkQueue = new KList<>(); this.latch = new ChronoLatch(3000); this.startTime = new AtomicLong(M.ms()); this.chunksPerSecond = new RollingSequence(10); @@ -81,7 +82,9 @@ public class DeepSearchPregenerator extends Thread implements Listener { foundChunks = new AtomicInteger(0); this.foundLast = new AtomicInteger(0); this.foundTotalChunks = new AtomicInteger((int) Math.ceil(Math.pow((2.0 * job.getRadiusBlocks()) / 16, 2))); - jobs.put(job.getWorld(), job); + + this.pos = 0; + jobs.put(job.getWorld().getName(), job); DeepSearchPregenerator.instance = this; } @@ -108,29 +111,22 @@ public class DeepSearchPregenerator extends Thread implements Listener { // chunkCache(); //todo finish this if (latch.flip() && !job.paused) { if (cacheLock.isLocked()) { - Iris.info("DeepFinder: Caching: " + chunkCachePos.get() + " Of " + chunkCacheSize.get()); + } else { + long eta = computeETA(); + save(); + int secondGenerated = foundChunks.get() - foundLast.get(); + foundLast.set(foundChunks.get()); + secondGenerated = secondGenerated / 3; + chunksPerSecond.put(secondGenerated); + chunksPerMinute.put(secondGenerated * 60); + Iris.info("DeepFinder: " + C.IRIS + world.getName() + C.RESET + " Searching: " + Form.f(foundChunks.get()) + " of " + Form.f(foundTotalChunks.get()) + " " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2)); } - long eta = computeETA(); - save(); - int secondGenerated = foundChunks.get() - foundLast.get(); - foundLast.set(foundChunks.get()); - secondGenerated = secondGenerated / 3; - chunksPerSecond.put(secondGenerated); - chunksPerMinute.put(secondGenerated * 60); - Iris.info("deepFinder: " + C.IRIS + world.getName() + C.RESET + " RTT: " + Form.f(foundChunks.get()) + " of " + Form.f(foundTotalChunks.get()) + " " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2)); } - if (foundChunks.get() >= foundTotalChunks.get()) { Iris.info("Completed DeepSearch!"); interrupt(); - } else { - int pos = job.getPosition() + 1; - job.setPosition(pos); - if (!job.paused) { - tickSearch(getChunk(pos)); - } } } @@ -141,22 +137,18 @@ public class DeepSearchPregenerator extends Thread implements Listener { private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - private void tickSearch(Position2 chunk) { - executorService.submit(() -> { - CountDownLatch latch = new CountDownLatch(1); - try { - findInChunk(world, chunk.getX(), chunk.getZ()); - } catch (IOException e) { - throw new RuntimeException(e); + private void queueSystem(Position2 chunk) { + if (chunkQueue.isEmpty()) { + for (int limit = 512; limit != 0; limit--) { + pos = job.getPosition() + 1; + chunkQueue.add(getChunk(pos)); } - Iris.verbose("Generated Async " + chunk); - latch.countDown(); + } else { + //MCAUtil.read(); + + } + - try { - latch.await(); - } catch (InterruptedException ignored) {} - foundChunks.addAndGet(1); - }); } private void findInChunk(World world, int x, int z) throws IOException { @@ -271,7 +263,7 @@ public class DeepSearchPregenerator extends Thread implements Listener { @Data @Builder public static class DeepSearchJob { - private String world; + private World world; @Builder.Default private int radiusBlocks = 5000; @Builder.Default diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java index 11128d04a..56ee44b05 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java @@ -19,7 +19,9 @@ package com.volmit.iris.core.pregenerator; import com.volmit.iris.Iris; +import com.volmit.iris.core.pack.IrisPack; import com.volmit.iris.core.tools.IrisPackBenchmarking; +import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; @@ -30,15 +32,12 @@ import com.volmit.iris.util.math.RollingSequence; import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Looper; -import lombok.Getter; -import lombok.Setter; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmark; public class IrisPregenerator { private final PregenTask task; @@ -50,6 +49,7 @@ public class IrisPregenerator { private final RollingSequence chunksPerSecond; private final RollingSequence chunksPerMinute; private final RollingSequence regionsPerMinute; + private final KList chunksPerSecondHistory; private static AtomicInteger generated; private final AtomicInteger generatedLast; private final AtomicInteger generatedLastMinute; @@ -62,8 +62,6 @@ public class IrisPregenerator { private final KSet net; private final ChronoLatch cl; private final ChronoLatch saveLatch = new ChronoLatch(30000); - static long long_generatedChunks = 0; - static long long_totalChunks = 0; public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) { this.listener = listenify(listener); @@ -80,6 +78,7 @@ public class IrisPregenerator { chunksPerSecond = new RollingSequence(10); chunksPerMinute = new RollingSequence(10); regionsPerMinute = new RollingSequence(10); + chunksPerSecondHistory = new KList<>(); generated = new AtomicInteger(0); generatedLast = new AtomicInteger(0); generatedLastMinute = new AtomicInteger(0); @@ -93,6 +92,7 @@ public class IrisPregenerator { int secondGenerated = generated.get() - generatedLast.get(); generatedLast.set(generated.get()); chunksPerSecond.put(secondGenerated); + chunksPerSecondHistory.add(secondGenerated); if (minuteLatch.flip()) { int minuteGenerated = generated.get() - generatedLastMinute.get(); @@ -100,8 +100,6 @@ public class IrisPregenerator { chunksPerMinute.put(minuteGenerated); regionsPerMinute.put((double) minuteGenerated / 1024D); } - long_generatedChunks = generated.get(); - long_totalChunks = totalChunks.get(); listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(), regionsPerMinute.getAverage(), @@ -112,10 +110,10 @@ public class IrisPregenerator { if (cl.flip()) { double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100; - if(benchmark) { - Iris.info(C.GREEN +"Benchmark: " + C.WHITE + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2), percentage); + if (!IrisPackBenchmarking.benchmarkInProgress) { + Iris.info("Pregen: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage); } else { - Iris.info("Pregen: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2), percentage); + Iris.info("Benchmarking: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage); } } return 1000; @@ -128,17 +126,10 @@ public class IrisPregenerator { // If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers) ((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) : // If no, use quick function (which is less accurate over time but responds better to the initial delay) - ((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000 // + ((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000 ); } - public static long getLongGeneratedChunks() { - return long_generatedChunks; - } - public static long getLongTotalChunks() { - return long_totalChunks; - } - public void close() { shutdown.set(true); @@ -151,6 +142,11 @@ public class IrisPregenerator { task.iterateRegions((x, z) -> visitRegion(x, z, true)); task.iterateRegions((x, z) -> visitRegion(x, z, false)); shutdown(); + if (!IrisPackBenchmarking.benchmarkInProgress) { + Iris.info(C.IRIS + "Pregen stopped."); + } else { + IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory); + } } private void checkRegions() { @@ -167,7 +163,10 @@ public class IrisPregenerator { generator.close(); ticker.interrupt(); listener.onClose(); - getMantle().trim(0, 0); + Mantle mantle = getMantle(); + if (mantle != null) { + mantle.trim(0, 0); + } } private void visitRegion(int x, int z, boolean regions) { diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/PregenTask.java b/core/src/main/java/com/volmit/iris/core/pregenerator/PregenTask.java index 4abe057f8..015a418a1 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/PregenTask.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/PregenTask.java @@ -35,6 +35,8 @@ public class PregenTask { private static final KList ORDER_CENTER = computeChunkOrder(); private static final KMap> ORDERS = new KMap<>(); + @Builder.Default + private boolean gui = false; @Builder.Default private Position2 center = new Position2(0, 0); @Builder.Default diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index caa1f05ce..f26ab913e 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -3,11 +3,11 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; -import org.apache.logging.log4j.core.util.ExecutorServices; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; -import javax.print.attribute.standard.Severity; +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; import java.io.File; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -15,11 +15,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.StringJoiner; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import static com.volmit.iris.Iris.getJavaVersion; import static com.volmit.iris.Iris.instance; @@ -84,7 +82,7 @@ public class ServerBootSFG { severityHigh++; } - if (getJavaVersion() != 17) { + if (!List.of(17, 21).contains(getJavaVersion())) { isJDK17 = false; joiner.add("Unsupported Java version"); severityMedium++; @@ -96,11 +94,11 @@ public class ServerBootSFG { severityMedium++; } - if (!hasPrivileges()){ - hasPrivileges = false; - joiner.add("Insufficient Privileges"); - severityMedium++; - } +// if (!hasPrivileges()){ +// hasPrivileges = false; +// joiner.add("Insufficient Privileges"); +// severityMedium++; +// } Some servers dont like this if (!enoughDiskSpace()){ hasEnoughDiskSpace = false; @@ -128,36 +126,25 @@ public class ServerBootSFG { } } - public static boolean isJDK() { - String path = System.getProperty("sun.boot.library.path"); - if (path != null) { - String javacPath = ""; - if (path.endsWith(File.separator + "bin")) { - javacPath = path; - } else { - int libIndex = path.lastIndexOf(File.separator + "lib"); - if (libIndex > 0) { - javacPath = path.substring(0, libIndex) + File.separator + "bin"; - } - } - if (checkJavac(javacPath)) - return true; - } - path = System.getProperty("java.home"); - return path != null && checkJavac(path + File.separator + "bin"); - } + public static boolean isJDK() { + try { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + // If the compiler is null, it means this is a JRE environment, not a JDK. + return compiler != null; + } catch (Exception ignored) {} + return false; + } public static boolean hasPrivileges() { - return true ; -// Path pv = Paths.get(Bukkit.getWorldContainer() + "iristest.json"); -// try (FileChannel fc = FileChannel.open(pv, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ, StandardOpenOption.WRITE)) { -// if (Files.isReadable(pv) && Files.isWritable(pv)) { -// return true; -// } -// } catch (Exception e) { -// return false; -// } -// return false; + Path pv = Paths.get(Bukkit.getWorldContainer() + "iristest.json"); + try (FileChannel fc = FileChannel.open(pv, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ, StandardOpenOption.WRITE)) { + if (Files.isReadable(pv) && Files.isWritable(pv)) { + return true; + } + } catch (Exception e) { + return false; + } + return false; } public static boolean enoughDiskSpace() { diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java index c9155c7b3..659c02bdd 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java @@ -37,7 +37,7 @@ public class UtilsSFG { } if (ServerBootSFG.unsuportedversion) { Iris.safeguard(C.RED + "Server Version"); - Iris.safeguard(C.RED + "- Iris only supports 1.19.2 > 1.20.2"); + Iris.safeguard(C.RED + "- Iris only supports 1.19.2 > 1.20.6"); } if (!ServerBootSFG.passedserversoftware) { Iris.safeguard(C.YELLOW + "Unsupported Server Software"); @@ -53,11 +53,11 @@ public class UtilsSFG { } if (!ServerBootSFG.isJDK17) { Iris.safeguard(C.YELLOW + "Unsupported java version"); - Iris.safeguard(C.YELLOW + "- Please consider using JDK 17 Instead of JDK " + Iris.getJavaVersion()); + Iris.safeguard(C.YELLOW + "- Please consider using JDK 17 (or 21 for 1.20.6) Instead of JDK " + Iris.getJavaVersion()); } if (ServerBootSFG.isJRE) { Iris.safeguard(C.YELLOW + "Unsupported Server JDK"); - Iris.safeguard(C.YELLOW + "- Please consider using JDK 17 Instead of JRE " + Iris.getJavaVersion()); + Iris.safeguard(C.YELLOW + "- Please consider using JDK 17 (or 21 for 1.20.6) Instead of JRE " + Iris.getJavaVersion()); } } } 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 de6d2ac81..e70e1c1d4 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 @@ -1,119 +1,142 @@ -/* - * 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.core.service; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.link.*; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.plugin.IrisService; -import lombok.Data; -import org.bukkit.Bukkit; -import org.bukkit.block.data.BlockData; -import org.bukkit.event.EventHandler; -import org.bukkit.event.server.PluginEnableEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.MissingResourceException; -import java.util.Optional; - -@Data -public class ExternalDataSVC implements IrisService { - - private KList providers = new KList<>(), activeProviders = new KList<>(); - - @Override - public void onEnable() { - Iris.info("Loading ExternalDataProvider..."); - Bukkit.getPluginManager().registerEvents(this, Iris.instance); - - providers.add(new OraxenDataProvider()); - if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { - Iris.info("Oraxen found, loading OraxenDataProvider..."); - } - providers.add(new ItemAdderDataProvider()); - if (Bukkit.getPluginManager().getPlugin("ItemAdder") != null) { - Iris.info("ItemAdder found, loading ItemAdderDataProvider..."); - } - providers.add(new ExecutableItemsDataProvider()); - if (Bukkit.getPluginManager().getPlugin("ExecutableItems") != null) { - Iris.info("ExecutableItems found, loading ExecutableItemsDataProvider..."); - } - - for (ExternalDataProvider p : providers) { - if (p.isReady()) { - activeProviders.add(p); - p.init(); - Iris.info("Enabled ExternalDataProvider for %s.", p.getPluginId()); - } - } - } - - @Override - public void onDisable() { - } - - @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 -> { - activeProviders.add(edp); - edp.init(); - Iris.info("Enabled ExternalDataProvider for %s.", edp.getPluginId()); - }); - } - } - - public Optional getBlockData(Identifier key) { - Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, false)).findFirst(); - if (provider.isEmpty()) - return Optional.empty(); - try { - return Optional.of(provider.get().getBlockData(key)); - } catch (MissingResourceException e) { - Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]"); - return Optional.empty(); - } - } - - public Optional getItemStack(Identifier key) { - Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, true)).findFirst(); - if (provider.isEmpty()) { - Iris.warn("No matching Provider found for modded material \"%s\"!", key); - return Optional.empty(); - } - try { - return Optional.of(provider.get().getItemStack(key)); - } catch (MissingResourceException e) { - Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]"); - return Optional.empty(); - } - } - - public Identifier[] getAllBlockIdentifiers() { - KList names = new KList<>(); - activeProviders.forEach(p -> names.add(p.getBlockTypes())); - return names.toArray(new Identifier[0]); - } - - public Identifier[] getAllItemIdentifiers() { - KList names = new KList<>(); - activeProviders.forEach(p -> names.add(p.getItemTypes())); - return names.toArray(new Identifier[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.core.service; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.link.*; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.plugin.IrisService; +import lombok.Data; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.event.EventHandler; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.MissingResourceException; +import java.util.Optional; + +@Data +public class ExternalDataSVC implements IrisService { + + private KList providers = new KList<>(), activeProviders = new KList<>(); + + @Override + public void onEnable() { + Iris.info("Loading ExternalDataProvider..."); + Bukkit.getPluginManager().registerEvents(this, Iris.instance); + + providers.add(new OraxenDataProvider()); + if (Bukkit.getPluginManager().getPlugin("Oraxen") != null) { + Iris.info("Oraxen found, loading OraxenDataProvider..."); + } + providers.add(new ItemAdderDataProvider()); + if (Bukkit.getPluginManager().getPlugin("ItemAdder") != null) { + Iris.info("ItemAdder found, loading ItemAdderDataProvider..."); + } + providers.add(new ExecutableItemsDataProvider()); + if (Bukkit.getPluginManager().getPlugin("ExecutableItems") != null) { + Iris.info("ExecutableItems found, loading ExecutableItemsDataProvider..."); + } + providers.add(new HMCLeavesDataProvider()); + if (Bukkit.getPluginManager().getPlugin("HMCLeaves") != null) { + Iris.info("BlockAdder found, loading HMCLeavesDataProvider..."); + } + providers.add(new MMOItemsDataProvider()); + if (Bukkit.getPluginManager().getPlugin("MMOItems") != null) { + Iris.info("MMOItems found, loading MMOItemsDataProvider..."); + } + providers.add(new EcoItemsDataProvider()); + if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) { + Iris.info("EcoItems found, loading EcoItemsDataProvider..."); + } + + for (ExternalDataProvider p : providers) { + if (p.isReady()) { + activeProviders.add(p); + p.init(); + Iris.info("Enabled ExternalDataProvider for %s.", p.getPluginId()); + } + } + } + + @Override + public void onDisable() { + } + + @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 -> { + activeProviders.add(edp); + edp.init(); + Iris.info("Enabled ExternalDataProvider for %s.", edp.getPluginId()); + }); + } + } + + public Optional getBlockData(Identifier key) { + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, false)).findFirst(); + if (provider.isEmpty()) + return Optional.empty(); + try { + return Optional.of(provider.get().getBlockData(key)); + } catch (MissingResourceException e) { + Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]"); + return Optional.empty(); + } + } + + public Optional getItemStack(Identifier key) { + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, true)).findFirst(); + if (provider.isEmpty()) { + Iris.warn("No matching Provider found for modded material \"%s\"!", key); + return Optional.empty(); + } + try { + return Optional.of(provider.get().getItemStack(key)); + } catch (MissingResourceException e) { + Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]"); + return Optional.empty(); + } + } + + public void processUpdate(Engine engine, Block block, Identifier blockId) { + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(blockId, false)).findFirst(); + if (provider.isEmpty()) { + Iris.warn("No matching Provider found for modded material \"%s\"!", blockId); + return; + } + provider.get().processUpdate(engine, block, blockId); + } + + public Identifier[] getAllBlockIdentifiers() { + KList names = new KList<>(); + activeProviders.forEach(p -> names.add(p.getBlockTypes())); + return names.toArray(new Identifier[0]); + } + + public Identifier[] getAllItemIdentifiers() { + KList names = new KList<>(); + activeProviders.forEach(p -> names.add(p.getItemTypes())); + return names.toArray(new Identifier[0]); + } +} 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 6f3f79535..2220ccc26 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 @@ -1,35 +1,76 @@ package com.volmit.iris.core.service; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; +import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.mantle.TectonicPlate; import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.plugin.IrisService; +import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.Looper; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; import org.bukkit.Bukkit; import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.ServerLoadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; +import org.checkerframework.checker.units.qual.A; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; public class IrisEngineSVC implements IrisService { + public static IrisEngineSVC instance; + public boolean isServerShuttingDown = false; + public boolean isServerLoaded = false; private static final AtomicInteger tectonicLimit = new AtomicInteger(30); - private final ReentrantLock lastUseLock = new ReentrantLock(); - private final KMap lastUse = new KMap<>(); + private ReentrantLock lastUseLock; + private KMap lastUse; + private List IrisWorlds; private Looper cacheTicker; private Looper trimTicker; private Looper unloadTicker; + private Looper updateTicker; + private PrecisionStopwatch trimAlive; + private PrecisionStopwatch unloadAlive; + public PrecisionStopwatch trimActiveAlive; + public PrecisionStopwatch unloadActiveAlive; + private AtomicInteger TotalTectonicPlates; + private AtomicInteger TotalQueuedTectonicPlates; + private AtomicInteger TotalNotQueuedTectonicPlates; + private AtomicBoolean IsUnloadAlive; + private AtomicBoolean IsTrimAlive; + ChronoLatch cl; + public List corruptedIrisWorlds = new ArrayList<>(); @Override public void onEnable() { + this.cl = new ChronoLatch(5000); + lastUse = new KMap<>(); + lastUseLock = new ReentrantLock(); + IrisWorlds = new ArrayList<>(); + IsUnloadAlive = new AtomicBoolean(true); + IsTrimAlive = new AtomicBoolean(true); + trimActiveAlive = new PrecisionStopwatch(); + unloadActiveAlive = new PrecisionStopwatch(); + trimAlive = new PrecisionStopwatch(); + unloadAlive = new PrecisionStopwatch(); + TotalTectonicPlates = new AtomicInteger(); + TotalQueuedTectonicPlates = new AtomicInteger(); + TotalNotQueuedTectonicPlates = new AtomicInteger(); tectonicLimit.set(2); long t = getHardware.getProcessMemory(); while (t > 200) { @@ -37,15 +78,69 @@ public class IrisEngineSVC implements IrisService { t = t - 200; } this.setup(); + this.TrimLogic(); + this.UnloadLogic(); + + trimAlive.begin(); + unloadAlive.begin(); + trimActiveAlive.begin(); + unloadActiveAlive.begin(); + + updateTicker.start(); cacheTicker.start(); - trimTicker.start(); - unloadTicker.start(); + //trimTicker.start(); + //unloadTicker.start(); + instance = this; + + } + + public void engineStatus() { + boolean trimAlive = trimTicker.isAlive(); + boolean unloadAlive = unloadTicker.isAlive(); + Iris.info("Status:"); + Iris.info("- Trim: " + trimAlive); + Iris.info("- Unload: " + unloadAlive); + } public static int getTectonicLimit() { return tectonicLimit.get(); } + @EventHandler + public void onWorldUnload(WorldUnloadEvent event) { + updateWorlds(); + } + + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + updateWorlds(); + } + + @EventHandler + public void onServerBoot(ServerLoadEvent event) { + isServerLoaded = true; + } + + @EventHandler + public void onPluginDisable(PluginDisableEvent event) { + if (event.getPlugin().equals(Iris.instance)) { + isServerShuttingDown = true; + } + } + + public void updateWorlds() { + for (World world : Bukkit.getWorlds()) { + try { + if (IrisToolbelt.access(world).getEngine() != null) { + IrisWorlds.add(world); + } + } catch (Exception e) { + // no + } + } + } + private void setup() { cacheTicker = new Looper() { @Override @@ -57,7 +152,7 @@ public class IrisEngineSVC implements IrisService { Long last = lastUse.get(key); if (last == null) continue; - if (now - last > 60000) { // 1 minute + if (now - last > 60000) { lastUse.remove(key); } } @@ -67,57 +162,115 @@ public class IrisEngineSVC implements IrisService { return 1000; } }; - trimTicker = new Looper() { - private final Supplier supplier = createSupplier(); + + updateTicker = new Looper() { @Override protected long loop() { - long start = System.currentTimeMillis(); try { - Engine engine = supplier.get(); - if (engine != null) { - engine.getMantle().trim(tectonicLimit.get() / lastUse.size()); + TotalQueuedTectonicPlates.set(0); + TotalNotQueuedTectonicPlates.set(0); + TotalTectonicPlates.set(0); + for (World world : IrisWorlds) { + Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine(); + TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload()); + TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions()); + TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount()); } - } catch (Throwable e) { - Iris.reportError(e); - // return -1; - } + if (!isServerShuttingDown && isServerLoaded) { + if (!trimTicker.isAlive()) { + Iris.info(C.RED + "TrimTicker found dead! Booting it up!"); + try { + TrimLogic(); + } catch (Exception e) { + Iris.error("What happened?"); + e.printStackTrace(); + } + } - int size = lastUse.size(); - long time = (size > 0 ? 1000/size : 1000) - (System.currentTimeMillis() - start); - if (time <= 0) - return 0; - return time; - } - }; - - unloadTicker = new Looper() { - private final Supplier supplier = createSupplier(); - - @Override - protected long loop() { - long start = System.currentTimeMillis(); - try { - Engine engine = supplier.get(); - if (engine != null) { - long unloadStart = System.currentTimeMillis(); - int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size()); - if (count > 0) { - Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); + if (!unloadTicker.isAlive()) { + Iris.info(C.RED + "UnloadTicker found dead! Booting it up!"); + try { + UnloadLogic(); + } catch (Exception e) { + Iris.error("What happened?"); + e.printStackTrace(); + } } } - } catch (Throwable e) { - Iris.reportError(e); + + } catch (Exception e) { return -1; } - - int size = lastUse.size(); - long time = (size > 0 ? 1000/size : 1000) - (System.currentTimeMillis() - start); - if (time <= 0) - return 0; - return time; + return 1000; } }; } + public void TrimLogic() { + if (trimTicker == null || !trimTicker.isAlive()) { + trimTicker = new Looper() { + private final Supplier supplier = createSupplier(); + + @Override + protected long loop() { + long start = System.currentTimeMillis(); + trimAlive.reset(); + try { + Engine engine = supplier.get(); + if (engine != null) { + engine.getMantle().trim(tectonicLimit.get() / lastUse.size()); + } + } catch (Throwable e) { + Iris.reportError(e); + Iris.info(C.RED + "EngineSVC: Failed to trim."); + e.printStackTrace(); + return -1; + } + + int size = lastUse.size(); + long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); + if (time <= 0) + return 0; + return time; + } + }; + trimTicker.start(); + } + } + public void UnloadLogic() { + if (unloadTicker == null || !unloadTicker.isAlive()) { + unloadTicker = new Looper() { + private final Supplier supplier = createSupplier(); + + @Override + protected long loop() { + long start = System.currentTimeMillis(); + unloadAlive.reset(); + try { + Engine engine = supplier.get(); + if (engine != null) { + long unloadStart = System.currentTimeMillis(); + int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size()); + if (count > 0) { + Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); + } + } + } catch (Throwable e) { + Iris.reportError(e); + Iris.info(C.RED + "EngineSVC: Failed to unload."); + e.printStackTrace(); + return -1; + } + + int size = lastUse.size(); + long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); + if (time <= 0) + return 0; + return time; + } + }; + unloadTicker.start(); + } + } private Supplier createSupplier() { AtomicInteger i = new AtomicInteger(); @@ -136,7 +289,8 @@ public class IrisEngineSVC implements IrisService { if (generator != null) { Engine engine = generator.getEngine(); - if (engine != null) { + boolean closed = engine.getMantle().getData().isClosed(); + if (engine != null && !engine.isStudio() && !closed) { lastUseLock.lock(); lastUse.put(world, System.currentTimeMillis()); lastUseLock.unlock(); @@ -145,6 +299,8 @@ public class IrisEngineSVC implements IrisService { } } } catch (Throwable e) { + Iris.info(C.RED + "EngineSVC: Failed to create supplier."); + e.printStackTrace(); Iris.reportError(e); } return null; diff --git a/core/src/main/java/com/volmit/iris/core/service/WandSVC.java b/core/src/main/java/com/volmit/iris/core/service/WandSVC.java index 27fc4c87a..e71392c89 100644 --- a/core/src/main/java/com/volmit/iris/core/service/WandSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/WandSVC.java @@ -30,9 +30,11 @@ import com.volmit.iris.util.format.C; import com.volmit.iris.util.math.M; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.WorldMatter; +import com.volmit.iris.util.misc.E; import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; +import com.volmit.iris.util.scheduling.S; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.enchantments.Enchantment; @@ -53,6 +55,9 @@ import java.util.ArrayList; import java.util.Objects; public class WandSVC implements IrisService { + private static final Particle CRIT_MAGIC = E.getOrDefault(Particle.class, "CRIT_MAGIC", "CRIT"); + private static final Particle REDSTONE = E.getOrDefault(Particle.class, "REDSTONE", "DUST"); + private static ItemStack dust; private static ItemStack wand; @@ -162,11 +167,11 @@ public class WandSVC implements IrisService { */ public static ItemStack createDust() { ItemStack is = new ItemStack(Material.GLOWSTONE_DUST); - is.addUnsafeEnchantment(Enchantment.ARROW_INFINITE, 1); + is.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 1); ItemMeta im = is.getItemMeta(); im.setDisplayName(C.BOLD + "" + C.YELLOW + "Dust of Revealing"); im.setUnbreakable(true); - im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_PLACED_ON, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_DESTROYS, ItemFlag.HIDE_ENCHANTS); + im.addItemFlags(ItemFlag.values()); im.setLore(new KList().qadd("Right click on a block to reveal it's placement structure!")); is.setItemMeta(im); @@ -206,11 +211,11 @@ public class WandSVC implements IrisService { */ public static ItemStack createWand(Location a, Location b) { ItemStack is = new ItemStack(Material.BLAZE_ROD); - is.addUnsafeEnchantment(Enchantment.ARROW_INFINITE, 1); + is.addUnsafeEnchantment(Enchantment.FIRE_ASPECT, 1); ItemMeta im = is.getItemMeta(); im.setDisplayName(C.BOLD + "" + C.GOLD + "Wand of Iris"); im.setUnbreakable(true); - im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_PLACED_ON, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_DESTROYS, ItemFlag.HIDE_ENCHANTS); + im.addItemFlags(ItemFlag.values()); im.setLore(new KList().add(locationToString(a), locationToString(b))); is.setItemMeta(im); @@ -311,7 +316,7 @@ public class WandSVC implements IrisService { */ public void draw(Location[] d, Player p) { Vector gx = Vector.getRandom().subtract(Vector.getRandom()).normalize().clone().multiply(0.65); - d[0].getWorld().spawnParticle(Particle.CRIT_MAGIC, d[0], 1, 0.5 + gx.getX(), 0.5 + gx.getY(), 0.5 + gx.getZ(), 0, null, false); + d[0].getWorld().spawnParticle(CRIT_MAGIC, d[0], 1, 0.5 + gx.getX(), 0.5 + gx.getY(), 0.5 + gx.getZ(), 0, null, false); Vector gxx = Vector.getRandom().subtract(Vector.getRandom()).normalize().clone().multiply(0.65); d[1].getWorld().spawnParticle(Particle.CRIT, d[1], 1, 0.5 + gxx.getX(), 0.5 + gxx.getY(), 0.5 + gxx.getZ(), 0, null, false); @@ -370,7 +375,7 @@ public class WandSVC implements IrisService { int r = color.getRed(); int g = color.getGreen(); int b = color.getBlue(); - p.spawnParticle(Particle.REDSTONE, lv.getX(), lv.getY(), lv.getZ(), 1, 0, 0, 0, 0, new Particle.DustOptions(org.bukkit.Color.fromRGB(r, g, b), 0.75f)); + p.spawnParticle(REDSTONE, lv.getX(), lv.getY(), lv.getZ(), 1, 0, 0, 0, 0, new Particle.DustOptions(org.bukkit.Color.fromRGB(r, g, b), 0.75f)); } } } diff --git a/core/src/main/java/com/volmit/iris/core/service/WorldLoadSFG.java b/core/src/main/java/com/volmit/iris/core/service/WorldLoadSFG.java deleted file mode 100644 index 845edf054..000000000 --- a/core/src/main/java/com/volmit/iris/core/service/WorldLoadSFG.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.volmit.iris.core.service; - -import com.volmit.iris.Iris; -import com.volmit.iris.util.plugin.IrisService; -import org.bukkit.World; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.plugin.java.JavaPlugin; - -import static java.lang.System.getLogger; - -public class WorldLoadSFG implements IrisService { - private JavaPlugin plugin; - @EventHandler - public void onWorldLoad(WorldLoadEvent event) { - World world = event.getWorld(); - - } - - @Override - public void onEnable() { - this.plugin = Iris.instance; - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public void onDisable() { - - } -} diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java b/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java index 300a4467f..79bf1b643 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java @@ -28,7 +28,6 @@ import java.io.InputStreamReader; import static com.google.common.math.LongMath.isPrime; import static com.volmit.iris.util.misc.getHardware.getCPUModel; -import static com.volmit.iris.util.misc.getHardware.getDiskModel; public class IrisBenchmarking { static String ServerOS; static String filePath = "benchmark.dat"; @@ -180,12 +179,12 @@ public class IrisBenchmarking { Iris.info("- Data Compression: " + formatDouble(calculateDataCompression) + " MBytes/Sec"); if (WindowsDiskSpeed) { - Iris.info("Disk Model: " + getDiskModel()); + //Iris.info("Disk Model: " + getDiskModel()); Iris.info(C.BLUE + "- Running with Windows System Assessment Tool"); Iris.info("- Sequential 64.0 Write: " + C.BLUE + formatDouble(avgWriteSpeedMBps) + " Mbps"); Iris.info("- Sequential 64.0 Read: " + C.BLUE + formatDouble(avgReadSpeedMBps) + " Mbps"); } else { - Iris.info("Disk Model: " + getDiskModel()); + // Iris.info("Disk Model: " + getDiskModel()); Iris.info(C.GREEN + "- Running in Native Mode"); Iris.info("- Average Write Speed: " + C.GREEN + formatDouble(avgWriteSpeedMBps) + " Mbps"); Iris.info("- Average Read Speed: " + C.GREEN + formatDouble(avgReadSpeedMBps) + " Mbps"); diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisConverter.java b/core/src/main/java/com/volmit/iris/core/tools/IrisConverter.java new file mode 100644 index 000000000..fb4d344eb --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisConverter.java @@ -0,0 +1,395 @@ +package com.volmit.iris.core.tools; + +import com.volmit.iris.Iris; +import com.volmit.iris.engine.object.*; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.nbt.io.NBTUtil; +import com.volmit.iris.util.nbt.io.NamedTag; +import com.volmit.iris.util.nbt.tag.*; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.reflect.V; +import com.volmit.iris.util.scheduling.J; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.util.Vector; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +public class IrisConverter { + public static void convertSchematics(VolmitSender sender) { + File folder = Iris.instance.getDataFolder("convert"); + + FilenameFilter filter = (dir, name) -> name.endsWith(".schem"); + File[] fileList = folder.listFiles(filter); + ExecutorService executorService = Executors.newFixedThreadPool(1); + executorService.submit(() -> { + for (File schem : fileList) { + try { + PrecisionStopwatch p = new PrecisionStopwatch(); + boolean largeObject = false; + NamedTag tag = null; + try { + tag = NBTUtil.read(schem); + } catch (IOException e) { + Iris.info(C.RED + "Failed to read: " + schem.getName()); + throw new RuntimeException(e); + } + CompoundTag compound = (CompoundTag) tag.getTag(); + + if (compound.containsKey("Palette") && compound.containsKey("Width") && compound.containsKey("Height") && compound.containsKey("Length")) { + int objW = ((ShortTag) compound.get("Width")).getValue(); + int objH = ((ShortTag) compound.get("Height")).getValue(); + int objD = ((ShortTag) compound.get("Length")).getValue(); + int mv = objW * objH * objD; + AtomicInteger v = new AtomicInteger(0); + AtomicInteger fv = new AtomicInteger(0); + if (mv > 500_000) { + largeObject = true; + Iris.info(C.GRAY + "Converting.. "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob")); + Iris.info(C.GRAY + "- It may take a while"); + if (sender.isPlayer()) { + J.a(() -> { +// while (v.get() != mv) { +// double pr = ((double) v.get() / (double ) mv); +// sender.sendProgress(pr, "Converting"); +// J.sleep(16); +// } + }); + } + } + + CompoundTag paletteTag = (CompoundTag) compound.get("Palette"); + Map blockmap = new HashMap<>(paletteTag.size(), 0.9f); + for (Map.Entry> entry : paletteTag.getValue().entrySet()) { + String blockName = entry.getKey(); + BlockData bd = Bukkit.createBlockData(blockName); + Tag blockTag = entry.getValue(); + int blockId = ((IntTag) blockTag).getValue(); + blockmap.put(blockId, bd); + } + + ByteArrayTag byteArray = (ByteArrayTag) compound.get("BlockData"); + byte[] originalBlockArray = byteArray.getValue(); + int b = 0; + int a = 0; + Map y = new HashMap<>(); + Map x = new HashMap<>(); + Map z = new HashMap<>(); + + // Height adjustments + for (int h = 0; h < objH; h++) { + if (b == 0) { + y.put(h, (byte) 0); + } + if (b > 0) { + y.put(h, (byte) 1); + } + a = 0; + b = 0; + for (int d = 0; d < objD; d++) { + for (int w = 0; w < objW; w++) { + BlockData db = blockmap.get((int) originalBlockArray[fv.get()]); + if(db.getAsString().contains("minecraft:air")) { + a++; + } else { + b++; + } + fv.getAndAdd(1); + } + } + } + fv.set(0); + + // Width adjustments + for (int w = 0; w < objW; w++) { + if (b == 0) { + x.put(w, (byte) 0); + } + if (b > 0) { + x.put(w, (byte) 1); + } + a = 0; + b = 0; + for (int h = 0; h < objH; h++) { + for (int d = 0; d < objD; d++) { + BlockData db = blockmap.get((int) originalBlockArray[fv.get()]); + if(db.getAsString().contains("minecraft:air")) { + a++; + } else { + b++; + } + fv.getAndAdd(1); + } + } + } + fv.set(0); + + // Depth adjustments + for (int d = 0; d < objD; d++) { + if (b == 0) { + z.put(d, (byte) 0); + } + if (b > 0) { + z.put(d, (byte) 1); + } + a = 0; + b = 0; + for (int h = 0; h < objH; h++) { + for (int w = 0; w < objW; w++) { + BlockData db = blockmap.get((int) originalBlockArray[fv.get()]); + if(db.getAsString().contains("minecraft:air")) { + a++; + } else { + b++; + } + fv.getAndAdd(1); + } + } + } + fv.set(0); + int CorrectObjH = getCorrectY(y, objH); + int CorrectObjW = getCorrectX(x, objW); + int CorrectObjD = getCorrectZ(z, objD); + + //IrisObject object = new IrisObject(CorrectObjW, CorrectObjH, CorrectObjH); + IrisObject object = new IrisObject(objW, objH, objD); + Vector originalVector = new Vector(objW,objH,objD); + + + int[] yc = null; + int[] xc = null; + int[] zc = null; + + + int fo = 0; + int so = 0; + int o = 0; + int c = 0; + for (Integer i : y.keySet()) { + if (y.get(i) == 0) { + o++; + } + if (y.get(i) == 1) { + c++; + if (c == 1) { + fo = o; + } + o = 0; + } + } + so = o; + yc = new int[]{fo, so}; + + fo = 0; + so = 0; + o = 0; + c = 0; + for (Integer i : x.keySet()) { + if (x.get(i) == 0) { + o++; + } + if (x.get(i) == 1) { + c++; + if (c == 1) { + fo = o; + } + o = 0; + } + } + so = o; + xc = new int[]{fo, so}; + + fo = 0; + so = 0; + o = 0; + c = 0; + for (Integer i : z.keySet()) { + if (z.get(i) == 0) { + o++; + } + if (z.get(i) == 1) { + c++; + if (c == 1) { + fo = o; + } + o = 0; + } + } + so = o; + zc = new int[]{fo, so}; + + int h1, h2, w1, w2, v1 = 0, volume = objW * objH * objD; + Map blockLocationMap = new LinkedHashMap<>(); + boolean hasAir = false; + int pos = 0; + for (int i : originalBlockArray) { + blockLocationMap.put(pos, i); + pos++; + } + + + + for (int h = 0; h < objH; h++) { + for (int d = 0; d < objD; d++) { + for (int w = 0; w < objW; w++) { + BlockData bd = blockmap.get((int) originalBlockArray[v.get()]); + if (!bd.getMaterial().isAir()) { + object.setUnsigned(w, h, d, bd); + } + v.getAndAdd(1); + } + } + } + + + try { + object.write(new File(folder, schem.getName().replace(".schem", ".iob"))); + } catch (IOException e) { + Iris.info(C.RED + "Failed to save: " + schem.getName()); + throw new RuntimeException(e); + } + if (sender.isPlayer()) { + if (largeObject) { + sender.sendMessage(C.IRIS + "Converted "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis())); + } else { + sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob")); + } + } + if (largeObject) { + Iris.info(C.GRAY + "Converted "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis())); + } else { + Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob")); + } + // schem.delete(); + } + } catch (Exception e) { + Iris.info(C.RED + "Failed to convert: " + schem.getName()); + if (sender.isPlayer()) { + sender.sendMessage(C.RED + "Failed to convert: " + schem.getName()); + } + e.printStackTrace(); + Iris.reportError(e); + } + } + }); + } + + public static boolean isNewPointFurther(int[] originalPoint, int[] oldPoint, int[] newPoint) { + int oX = oldPoint[1]; + int oY = oldPoint[2]; + int oZ = oldPoint[3]; + + int nX = newPoint[1]; + int nY = newPoint[2]; + int nZ = newPoint[3]; + + int orX = originalPoint[1]; + int orY = originalPoint[2]; + int orZ = originalPoint[3]; + + double oldDistance = Math.sqrt(Math.pow(oX - orX, 2) + Math.pow(oY - orY, 2) + Math.pow(oZ - orZ, 2)); + double newDistance = Math.sqrt(Math.pow(nX - orX, 2) + Math.pow(nY - orY, 2) + Math.pow(nZ - orZ, 2)); + + if (newDistance > oldDistance) { + return true; + } + return false; + } + + public static int[] getCoordinates(int pos, int obX, int obY, int obZ) { + int z = 0; + int[] coords = new int[4]; + for (int h = 0; h < obY; h++) { + for (int d = 0; d < obZ; d++) { + for (int w = 0; w < obX; w++) { + if (z == pos) { + coords[1] = w; + coords[2] = h; + coords[3] = d; + return coords; + } + z++; + } + } + } + return null; + } + + public static int getCorrectY(Map y, int H) { + int fo = 0; + int so = 0; + int o = 0; + int c = 0; + for (Integer i : y.keySet()) { + if (y.get(i) == 0) { + o++; + } + if (y.get(i) == 1) { + c++; + if(c == 1){ + fo = o; + } + o = 0; + } + } + so = o; + return H = H - (fo + so); + } + + public static int getCorrectX(Map x, int W) { + int fo = 0; + int so = 0; + int o = 0; + int c = 0; + for (Integer i : x.keySet()) { + if (x.get(i) == 0) { + o++; + } + if (x.get(i) == 1) { + c++; + if(c == 1){ + fo = o; + } + o = 0; + } + } + so = o; + return W = W - (fo + so); + } + + public static int getCorrectZ(Map z, int D) { + int fo = 0; + int so = 0; + int o = 0; + int c = 0; + for (Integer i : z.keySet()) { + if (z.get(i) == 0) { + o++; + } + if (z.get(i) == 1) { + c++; + if(c == 1){ + fo = o; + } + o = 0; + } + } + so = o; + return D = D - (fo + so); + } +} + + + diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java index 7e6285d07..6096582d2 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java @@ -46,10 +46,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmark; import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; /** @@ -86,6 +82,11 @@ public class IrisCreator { * the world itself. Studio worlds are deleted when they are unloaded. */ private boolean studio = false; + /** + * Benchmark mode + */ + private boolean benchmark = false; + private boolean smartVanillaHeight = false; public static boolean removeFromBukkitYml(String name) throws IOException { YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML); @@ -110,15 +111,8 @@ public class IrisCreator { * @return the IrisAccess * @throws IrisException shit happens */ - IrisPackBenchmarking PackBench = new IrisPackBenchmarking(); + public World create() throws IrisException { - if (unstablemode){ - Iris.info(C.RED + "Your server is experiencing an incompatibility with the Iris plugin. Please rectify this problem to avoid further complications."); - Iris.info(C.RED + "----------------------------------------------------------------"); - Iris.info(C.RED + "Operation ran: Loading Iris World.."); - UtilsSFG.printIncompatibleWarnings(); - Iris.info(C.RED + "----------------------------------------------------------------"); - } if (Bukkit.isPrimaryThread()) { throw new IrisException("You cannot invoke create() on the main thread."); } @@ -149,6 +143,7 @@ public class IrisCreator { .name(name) .seed(seed) .studio(studio) + .smartVanillaHeight(smartVanillaHeight) .create(); ServerConfigurator.installDataPacks(false); @@ -193,7 +188,7 @@ public class IrisCreator { done.set(true); - if (sender.isPlayer()) { + if (sender.isPlayer() && !benchmark) { J.s(() -> { sender.player().teleport(new Location(world.get(), 0, world.get().getHighestBlockYAt(0, 0), 0)); }); diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java b/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java index 13577cc82..7993cf6e7 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java @@ -2,169 +2,200 @@ package com.volmit.iris.core.tools; import com.volmit.iris.Iris; -import com.volmit.iris.core.pregenerator.IrisPregenerator; -import com.volmit.iris.core.pregenerator.LazyPregenerator; import com.volmit.iris.core.pregenerator.PregenTask; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.exceptions.IrisException; -import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.scheduling.J; -import org.apache.commons.io.FileUtils; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import lombok.Getter; import org.bukkit.Bukkit; import java.io.File; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import static com.volmit.iris.core.commands.CommandIris.BenchDimension; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.Clock; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.concurrent.*; public class IrisPackBenchmarking { - public static boolean loaded = false; - public static boolean benchmark = false; - static boolean cancelled = false; - static boolean pregenInProgress = false; - static long startTime; - static long totalChunks; - static long generatedChunks; - static double elapsedTimeNs; + @Getter + public static IrisPackBenchmarking instance; + public static boolean benchmarkInProgress = false; + private IrisDimension IrisDimension; + private int radius; + private boolean finished = false; + PrecisionStopwatch stopwatch; - public static void runBenchmark() { - // IrisPackBenchmarking IrisPackBenchmarking = new IrisPackBenchmarking(); - benchmark = true; - Iris.info(C.BLUE + "Benchmarking Dimension: " + C.AQUA + BenchDimension); - //progress(); - CompletableFuture future = CompletableFuture.runAsync(() -> { - Iris.info(C.GOLD + "Setting everything up.."); - try { - String BenchmarkFolder = "\\Benchmark"; - File folder = new File(BenchmarkFolder); - if (folder.exists() && folder.isDirectory()) { - FileUtils.deleteDirectory(folder); - Iris.debug("Deleted old Benchmark"); - } else { - Iris.info(C.GOLD + "Old Benchmark not found!"); - if(folder.exists()){ - Iris.info(C.RED + "FAILED To remove old Benchmark!"); - //cancelled = true; + public IrisPackBenchmarking(IrisDimension dimension, int r) { + instance = this; + this.IrisDimension = dimension; + this.radius = r; + runBenchmark(); + } - } + private void runBenchmark() { + this.stopwatch = new PrecisionStopwatch(); + ExecutorService service = Executors.newSingleThreadExecutor(); + service.submit(() -> { + Iris.info("Setting up benchmark environment "); + benchmarkInProgress = true; + File file = new File("benchmark"); + if (file.exists()) { + deleteDirectory(file.toPath()); + } + createBenchmark(); + while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) { + J.sleep(1000); + Iris.debug("Iris PackBenchmark: Waiting..."); + } + Iris.info("Starting Benchmark!"); + stopwatch.begin(); + startBenchmark(); + }); + + } + + public boolean getBenchmarkInProgress() { + return benchmarkInProgress; + } + + public void finishedBenchmark(KList cps) { + try { + String time = Form.duration(stopwatch.getMillis()); + Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine(); + Iris.info("-----------------"); + Iris.info("Results:"); + Iris.info("- Total time: " + time); + Iris.info("- Average CPS: " + calculateAverage(cps)); + Iris.info(" - Median CPS: " + calculateMedian(cps)); + Iris.info(" - Highest CPS: " + findHighest(cps)); + Iris.info(" - Lowest CPS: " + findLowest(cps)); + Iris.info("-----------------"); + Iris.info("Creating a report.."); + File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks"); + profilers.mkdir(); + + File results = new File("plugins " + File.separator + "Iris", IrisDimension.getName() + LocalDateTime.now(Clock.systemDefaultZone()) + ".txt"); + results.createNewFile(); + KMap metrics = engine.getMetrics().pull(); + try (FileWriter writer = new FileWriter(results)) { + writer.write("-----------------\n"); + writer.write("Results:\n"); + writer.write("Dimension: " + IrisDimension.getName() + "\n"); + writer.write("- Date of Benchmark: " + LocalDateTime.now(Clock.systemDefaultZone()) + "\n"); + writer.write("\n"); + writer.write("Metrics"); + for (String m : metrics.k()) { + double i = metrics.get(m); + writer.write("- " + m + ": " + i); } - } catch (Exception e) { - throw new RuntimeException(); + writer.write("- " + metrics); + writer.write("Benchmark: " + LocalDateTime.now(Clock.systemDefaultZone()) + "\n"); + writer.write("- Total time: " + time + "\n"); + writer.write("- Average CPS: " + calculateAverage(cps) + "\n"); + writer.write(" - Median CPS: " + calculateMedian(cps) + "\n"); + writer.write(" - Highest CPS: " + findHighest(cps) + "\n"); + writer.write(" - Lowest CPS: " + findLowest(cps) + "\n"); + writer.write("-----------------\n"); + Iris.info("Finished generating a report!"); + } catch (IOException e) { + Iris.error("An error occurred writing to the file."); + e.printStackTrace(); } - }).thenRun(() -> { - Iris.info(C.GOLD + "Creating Benchmark Environment"); - createBenchmark(); - - }).thenRun(() -> { - Iris.info( C.BLUE + "Benchmark Started!"); - boolean done = false; - startBenchmarkTimer(); - startBenchmark(); - basicScheduler(); - }).thenRun(() -> { - - }); - // cancelled = future.cancel(true); - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { + Bukkit.getServer().unloadWorld("benchmark", true); + stopwatch.end(); + } catch (Exception e) { + Iris.error("Something has gone wrong!"); e.printStackTrace(); } } - - private static void results(){ - double averageCps = calculateAverageCPS(); - Iris.info("Benchmark Dimension: " + BenchDimension); - Iris.info("Speeds"); - Iris.info("- Average CPS: " + roundToTwoDecimalPlaces(averageCps)); - Iris.info("Duration: " + roundToTwoDecimalPlaces(elapsedTimeNs)); - - } - private static void basicScheduler() { - while (true) { - totalChunks = IrisPregenerator.getLongTotalChunks(); - generatedChunks = IrisPregenerator.getLongGeneratedChunks(); - if(totalChunks > 0) { - if (generatedChunks >= totalChunks) { - Iris.info("Benchmark Completed!"); - elapsedTimeNs = stopBenchmarkTimer(); - results(); - break; - } - } - //J.sleep(100); test - } - } - static void createBenchmark(){ + private void createBenchmark(){ try { IrisToolbelt.createWorld() - .dimension(BenchDimension) - .name("Benchmark") + .dimension(IrisDimension.getName()) + .name("benchmark") .seed(1337) .studio(false) + .benchmark(true) .create(); } catch (IrisException e) { throw new RuntimeException(e); } } - static void startBenchmark(){ + + private void startBenchmark(){ int x = 0; int z = 0; IrisToolbelt.pregenerate(PregenTask .builder() + .gui(false) .center(new Position2(x, z)) .width(5) .height(5) - .build(), Bukkit.getWorld("Benchmark") + .build(), Bukkit.getWorld("benchmark") ); } - static void startLazyBenchmark(){ - int x = 0; - int z = 0; - LazyPregenerator.LazyPregenJob pregenJob = LazyPregenerator.LazyPregenJob.builder() - //.world("Benchmark") - .healingPosition(0) - .healing(false) - .chunksPerMinute(3200) - .radiusBlocks(5000) - .position(0) - .build(); - LazyPregenerator pregenerator = new LazyPregenerator(pregenJob, new File("plugins/Iris/lazygen.json")); - pregenerator.start(); - } - public static double calculateAverageCPS() { - double elapsedTimeSec = elapsedTimeNs / 1_000_000_000.0; // Convert to seconds - return generatedChunks / elapsedTimeSec; - } - - private static void startBenchmarkTimer() { - startTime = System.nanoTime(); - } - - private static double stopBenchmarkTimer() { - long endTime = System.nanoTime(); - return (endTime - startTime) / 1_000_000_000.0; - } - - public static void deleteDirectory(File dir) { - File[] files = dir.listFiles(); - if(files != null) { - for(File file: files) { - if(file.isDirectory()) { - deleteDirectory(file); - } else { - file.delete(); - } - } + private double calculateAverage(KList list) { + double sum = 0; + for (int num : list) { + sum += num; } - dir.delete(); + return sum / list.size(); } - private static double roundToTwoDecimalPlaces(double value) { - return Double.parseDouble(String.format("%.2f", value)); + + private double calculateMedian(KList list) { + Collections.sort(list); + int middle = list.size() / 2; + + if (list.size() % 2 == 1) { + return list.get(middle); + } else { + return (list.get(middle - 1) + list.get(middle)) / 2.0; + } + } + + private int findLowest(KList list) { + return Collections.min(list); + } + + private int findHighest(KList list) { + return Collections.max(list); + } + + private boolean deleteDirectory(Path dir) { + try { + Files.walkFileTree(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } } } \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java index cd6c17a91..b815d3809 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java @@ -32,6 +32,7 @@ public class IrisWorldCreator { private String name; private boolean studio = false; private String dimensionName = null; + private boolean smartVanillaHeight = false; private long seed = 1337; public IrisWorldCreator() { @@ -63,6 +64,11 @@ public class IrisWorldCreator { return this; } + public IrisWorldCreator smartVanillaHeight(boolean smartVanillaHeight) { + this.smartVanillaHeight = smartVanillaHeight; + return this; + } + public WorldCreator create() { IrisDimension dim = IrisData.loadAnyDimension(dimensionName); @@ -76,7 +82,7 @@ public class IrisWorldCreator { .build(); ChunkGenerator g = new BukkitChunkGenerator(w, studio, studio ? dim.getLoader().getDataFolder() : - new File(w.worldFolder(), "iris/pack"), dimensionName); + new File(w.worldFolder(), "iris/pack"), dimensionName, smartVanillaHeight); return new WorldCreator(name) diff --git a/core/src/main/java/com/volmit/iris/core/wand/WandSelection.java b/core/src/main/java/com/volmit/iris/core/wand/WandSelection.java index 94be207d2..e2b99633b 100644 --- a/core/src/main/java/com/volmit/iris/core/wand/WandSelection.java +++ b/core/src/main/java/com/volmit/iris/core/wand/WandSelection.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.wand; import com.volmit.iris.util.data.Cuboid; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.misc.E; import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.entity.Player; @@ -29,6 +30,7 @@ import org.bukkit.util.Vector; import java.awt.*; public class WandSelection { + private static final Particle REDSTONE = E.getOrDefault(Particle.class, "REDSTONE", "DUST"); private final Cuboid c; private final Player p; @@ -101,7 +103,7 @@ public class WandSelection { int g = color.getGreen(); int b = color.getBlue(); - p.spawnParticle(Particle.REDSTONE, a.getX(), a.getY(), a.getZ(), + p.spawnParticle(REDSTONE, a.getX(), a.getY(), a.getZ(), 1, 0, 0, 0, 0, new Particle.DustOptions(org.bukkit.Color.fromRGB(r, g, b), (float) dist * 3f)); 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 db92954a8..3b271951f 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java @@ -292,7 +292,7 @@ public class IrisComplex implements DataProvider { double b = 0; for (IrisGenerator gen : generators) { - b += bx.getGenLinkMax(gen.getLoadKey()); + b += bx.getGenLinkMax(gen.getLoadKey(), engine); } return b; @@ -311,7 +311,7 @@ public class IrisComplex implements DataProvider { double b = 0; for (IrisGenerator gen : generators) { - b += bx.getGenLinkMin(gen.getLoadKey()); + b += bx.getGenLinkMin(gen.getLoadKey(), engine); } return b; 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 f61d90531..8e28490d8 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java @@ -19,8 +19,6 @@ package com.volmit.iris.engine; import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.mantle.EngineMantle; @@ -47,9 +45,6 @@ import java.io.IOException; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmark; -import static com.volmit.iris.core.safeguard.PerformanceSFG.*; - @Data @EqualsAndHashCode(exclude = "engine") @ToString(exclude = "engine") @@ -290,17 +285,10 @@ public class IrisEngineMantle implements EngineMantle { x = Math.max(x, c); x = (Math.max(x, 16) + 16) >> 4; x = x % 2 == 0 ? x + 1 : x; - if (benchmark){ - x = 4; - Iris.info("Mantle Size: " + x + " Chunks " + C.BLUE + "BENCHMARK MODE"); - } else { - Iris.info("Mantle Size: " + x + " Chunks"); - Iris.info(" Object Mantle Size: " + u + " (" + ((Math.max(u, 16) + 16) >> 4) + ")"); - Iris.info(" Jigsaw Mantle Size: " + jig + " (" + ((Math.max(jig, 16) + 16) >> 4) + ")"); - Iris.info(" Carving Mantle Size: " + c + " (" + ((Math.max(c, 16) + 16) >> 4) + ")"); - } - - + Iris.info("Mantle Size: " + x + " Chunks"); + Iris.info(" Object Mantle Size: " + u + " (" + ((Math.max(u, 16) + 16) >> 4) + ")"); + Iris.info(" Jigsaw Mantle Size: " + jig + " (" + ((Math.max(jig, 16) + 16) >> 4) + ")"); + Iris.info(" Carving Mantle Size: " + c + " (" + ((Math.max(c, 16) + 16) >> 4) + ")"); return x; } diff --git a/core/src/main/java/com/volmit/iris/engine/data/chunk/LinkedTerrainChunk.java b/core/src/main/java/com/volmit/iris/engine/data/chunk/LinkedTerrainChunk.java index cbf18c1e6..015efd0e9 100644 --- a/core/src/main/java/com/volmit/iris/engine/data/chunk/LinkedTerrainChunk.java +++ b/core/src/main/java/com/volmit/iris/engine/data/chunk/LinkedTerrainChunk.java @@ -21,6 +21,7 @@ package com.volmit.iris.engine.data.chunk; import com.volmit.iris.core.nms.BiomeBaseInjector; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.util.data.IrisBiomeStorage; +import com.volmit.iris.util.data.IrisBlockData; import lombok.Setter; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -120,6 +121,8 @@ public class LinkedTerrainChunk implements TerrainChunk { @Override public synchronized void setBlock(int x, int y, int z, BlockData blockData) { + if (blockData instanceof IrisBlockData d) + blockData = d.getBase(); rawChunkData.setBlock(x, y, z, blockData); } diff --git a/core/src/main/java/com/volmit/iris/engine/data/chunk/MCATerrainChunk.java b/core/src/main/java/com/volmit/iris/engine/data/chunk/MCATerrainChunk.java index 31b24a665..2da3db43a 100644 --- a/core/src/main/java/com/volmit/iris/engine/data/chunk/MCATerrainChunk.java +++ b/core/src/main/java/com/volmit/iris/engine/data/chunk/MCATerrainChunk.java @@ -20,6 +20,7 @@ package com.volmit.iris.engine.data.chunk; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.BiomeBaseInjector; +import com.volmit.iris.util.data.IrisBlockData; import com.volmit.iris.util.nbt.mca.Chunk; import com.volmit.iris.util.nbt.mca.NBTWorld; import lombok.AllArgsConstructor; @@ -88,6 +89,8 @@ public class MCATerrainChunk implements TerrainChunk { if (blockData == null) { Iris.error("NULL BD"); } + if (blockData instanceof IrisBlockData data) + blockData = data.getBase(); mcaChunk.setBlockStateAt(xx, y, zz, NBTWorld.getCompound(blockData), false); } 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 4d57cdf83..e1f6560dc 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 @@ -22,10 +22,12 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.gui.components.RenderType; import com.volmit.iris.core.gui.components.Renderer; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.loader.IrisData; 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.service.ExternalDataSVC; import com.volmit.iris.engine.IrisComplex; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.chunk.TerrainChunk; @@ -38,6 +40,7 @@ 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.DataProvider; +import com.volmit.iris.util.data.IrisBlockData; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.format.C; @@ -254,6 +257,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat if (B.isUpdatable(data)) { getMantle().updateBlock(x, y, z); } + if (data instanceof IrisBlockData d) { + getMantle().getMantle().set(x, y, z, d.getCustom()); + } else { + getMantle().getMantle().remove(x, y, z, Identifier.class); + } } void blockUpdatedMetric(); @@ -277,6 +285,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), tile.getData().getTileId()); }); })); + getMantle().getMantle().raiseFlag(c.getX(), c.getZ(), MantleFlag.CUSTOM, () -> J.s(() -> { + getMantle().getMantle().iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> { + Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v); + }); + })); getMantle().getMantle().raiseFlag(c.getX(), c.getZ(), MantleFlag.UPDATE, () -> J.s(() -> { PrecisionStopwatch p = PrecisionStopwatch.start(); diff --git a/core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java b/core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java new file mode 100644 index 000000000..16968eee5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/framework/placer/WorldObjectPlacer.java @@ -0,0 +1,127 @@ +package com.volmit.iris.engine.framework.placer; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.data.cache.Cache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.IrisLootEvent; +import com.volmit.iris.engine.mantle.EngineMantle; +import com.volmit.iris.engine.object.IObjectPlacer; +import com.volmit.iris.engine.object.InventorySlotType; +import com.volmit.iris.engine.object.IrisLootTable; +import com.volmit.iris.engine.object.TileData; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.data.B; +import com.volmit.iris.util.math.RNG; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.TileState; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.InventoryHolder; + +@Getter +@EqualsAndHashCode(exclude = {"engine", "mantle"}) +public class WorldObjectPlacer implements IObjectPlacer { + private final World world; + private final Engine engine; + private final EngineMantle mantle; + + public WorldObjectPlacer(World world) { + var a = IrisToolbelt.access(world); + if (a == null || a.getEngine() == null) throw new IllegalStateException(world.getName() + " is not an Iris World!"); + this.world = world; + this.engine = a.getEngine(); + this.mantle = engine.getMantle(); + } + + @Override + public int getHighest(int x, int z, IrisData data) { + return mantle.getHighest(x, z, data); + } + + @Override + public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) { + return mantle.getHighest(x, z, data, ignoreFluid); + } + + @Override + public void set(int x, int y, int z, BlockData d) { + Block block = world.getBlockAt(x, y + world.getMinHeight(), z); + + if (y <= world.getMinHeight() || block.getType() == Material.BEDROCK) return; + InventorySlotType slot = null; + if (B.isStorageChest(d)) { + slot = InventorySlotType.STORAGE; + } + + if (slot != null) { + RNG rx = new RNG(Cache.key(x, z)); + KList tables = engine.getLootTables(rx, block); + + try { + Bukkit.getPluginManager().callEvent(new IrisLootEvent(engine, block, slot, tables)); + + if (!tables.isEmpty()){ + Iris.debug("IrisLootEvent has been accessed"); + } + + if (tables.isEmpty()) + return; + InventoryHolder m = (InventoryHolder) block.getState(); + engine.addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15); + } catch (Throwable e) { + Iris.reportError(e); + } + } + + block.setBlockData(d); + } + + @Override + public BlockData get(int x, int y, int z) { + return world.getBlockAt(x, y + world.getMinHeight(), z).getBlockData(); + } + + @Override + public boolean isPreventingDecay() { + return mantle.isPreventingDecay(); + } + + @Override + public boolean isCarved(int x, int y, int z) { + return mantle.isCarved(x, y, z); + } + + @Override + public boolean isSolid(int x, int y, int z) { + return world.getBlockAt(x, y + world.getMinHeight(), z).getType().isSolid(); + } + + @Override + public boolean isUnderwater(int x, int z) { + return mantle.isUnderwater(x, z); + } + + @Override + public int getFluidHeight() { + return mantle.getFluidHeight(); + } + + @Override + public boolean isDebugSmartBore() { + return mantle.isDebugSmartBore(); + } + + @Override + public void setTile(int xx, int yy, int zz, TileData tile) { + BlockState state = world.getBlockAt(xx, yy + world.getMinHeight(), zz).getState(); + tile.toBukkitTry(state); + state.update(); + } +} diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java index e93fe5c7d..5a966e752 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedPiece.java @@ -20,29 +20,19 @@ package com.volmit.iris.engine.jigsaw; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.*; -import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.context.IrisContext; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.math.AxisAlignedBB; -import com.volmit.iris.util.math.BlockPosition; -import com.volmit.iris.util.math.RNG; +import lombok.AccessLevel; import lombok.Data; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.block.TileState; -import org.bukkit.block.data.BlockData; -import org.bukkit.inventory.InventoryHolder; +import lombok.EqualsAndHashCode; +import lombok.Setter; import org.bukkit.util.BlockVector; import java.util.ArrayList; import java.util.List; -@SuppressWarnings("ALL") @Data public class PlannedPiece { private IrisPosition position; @@ -50,11 +40,19 @@ public class PlannedPiece { private IrisObject ogObject; private IrisJigsawPiece piece; private IrisObjectRotation rotation; + @EqualsAndHashCode.Exclude private IrisData data; private KList connected; private boolean dead = false; private AxisAlignedBB box; + @EqualsAndHashCode.Exclude private PlannedStructure structure; + @EqualsAndHashCode.Exclude + @Setter(AccessLevel.NONE) + private ParentConnection parent = null; + @EqualsAndHashCode.Exclude + @Setter(AccessLevel.NONE) + private KMap realPositions; public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece) { this(structure, position, piece, 0, 0, 0); @@ -76,6 +74,7 @@ public class PlannedPiece { this.object.setLoadKey(piece.getObject()); this.ogObject.setLoadKey(piece.getObject()); this.connected = new KList<>(); + this.realPositions = new KMap<>(); } @@ -94,7 +93,15 @@ public class PlannedPiece { } BlockVector v = getObject().getCenter(); - box = object.getAABB().shifted(position.add(new IrisPosition(object.getCenter()))); + IrisPosition pos = new IrisPosition(); + IrisObjectPlacement options = piece.getPlacementOptions(); + if (options != null && options.getTranslate() != null) { + IrisObjectTranslate translate = options.getTranslate(); + pos.setX(translate.getX()); + pos.setY(translate.getY()); + pos.setZ(translate.getZ()); + } + box = object.getAABB().shifted(position.add(new IrisPosition(object.getCenter())).add(pos)); return box; } @@ -126,11 +133,21 @@ public class PlannedPiece { return c; } - public boolean connect(IrisJigsawPieceConnector c) { - if (piece.getConnectors().contains(c)) { - return connected.addIfMissing(c); - } + public KList getChildConnectors() { + ParentConnection pc = getParent(); + KList c = getConnected().copy(); + if (pc != null) c.removeIf(i -> i.equals(pc.connector)); + return c; + } + public boolean connect(IrisJigsawPieceConnector c, PlannedPiece p, IrisJigsawPieceConnector pc) { + if (piece.getConnectors().contains(c) && p.getPiece().getConnectors().contains(pc)) { + if (connected.contains(c) || p.connected.contains(pc)) return false; + connected.add(c); + p.connected.add(pc); + p.parent = new ParentConnection(this, c, p, pc); + return true; + } return false; } @@ -162,108 +179,29 @@ public class PlannedPiece { } public boolean isFull() { - return connected.size() >= piece.getConnectors().size() || isDead(); + return connected.size() >= piece.getConnectors().size(); } - public void place(World world) { - PlatformChunkGenerator a = IrisToolbelt.access(world); - - int minY = 0; - if (a != null) { - minY = a.getEngine().getMinHeight(); - - if (!a.getEngine().getDimension().isBedrock()) - minY--; //If the dimension has no bedrock, allow it to go a block lower + public void setRealPositions(int x, int y, int z, IObjectPlacer placer) { + boolean isUnderwater = piece.getPlacementOptions().isUnderwater(); + for (IrisJigsawPieceConnector c : piece.getConnectors()) { + var pos = c.getPosition().add(new IrisPosition(x, 0, z)); + if (y < 0) { + pos.setY(pos.getY() + placer.getHighest(pos.getX(), pos.getZ(), getData(), isUnderwater) + (object.getH() / 2)); + } else { + pos.setY(pos.getY() + y); + } + realPositions.put(c, pos); } + } - getPiece().getPlacementOptions().setTranslate(new IrisObjectTranslate()); - getPiece().getPlacementOptions().setRotation(rotation); - int finalMinY = minY; - RNG rng = getStructure().getRng().nextParallelRNG(37555); - - // TODO: REAL CLASSES!!!!!!! - getOgObject().place(position.getX() + getObject().getCenter().getBlockX(), position.getY() + getObject().getCenter().getBlockY(), position.getZ() + getObject().getCenter().getBlockZ(), new IObjectPlacer() { - @Override - public int getHighest(int x, int z, IrisData data) { - return position.getY(); - } - - @Override - public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) { - return position.getY(); - } - - @Override - public void set(int x, int y, int z, BlockData d) { - Block block = world.getBlockAt(x, y, z); - - //Prevent blocks being set in or bellow bedrock - if (y <= finalMinY || block.getType() == Material.BEDROCK) return; - - block.setBlockData(d); - - if (a != null && getPiece().getPlacementOptions().getLoot().isNotEmpty() && - block.getState() instanceof InventoryHolder) { - - IrisLootTable table = getPiece().getPlacementOptions().getTable(block.getBlockData(), getData()); - if (table == null) return; - Engine engine = a.getEngine(); - engine.addItems(false, ((InventoryHolder) block.getState()).getInventory(), - rng.nextParallelRNG(BlockPosition.toLong(x, y, z)), - new KList<>(table), InventorySlotType.STORAGE, x, y, z, 15); - } - } - - @Override - public BlockData get(int x, int y, int z) { - return world.getBlockAt(x, y, z).getBlockData(); - } - - @Override - public boolean isPreventingDecay() { - return false; - } - - @Override - public boolean isCarved(int x, int y, int z) { - return false; - } - - @Override - public boolean isSolid(int x, int y, int z) { - return world.getBlockAt(x, y, z).getType().isSolid(); - } - - @Override - public boolean isUnderwater(int x, int z) { - return false; - } - - @Override - public int getFluidHeight() { - return 0; - } - - @Override - public boolean isDebugSmartBore() { - return false; - } - - @Override - public void setTile(int xx, int yy, int zz, TileData tile) { - BlockState state = world.getBlockAt(xx, yy, zz).getState(); - tile.toBukkitTry(state); - state.update(); - } - - @Override - public Engine getEngine() { - if (IrisToolbelt.isIrisWorld(world)) { - return IrisToolbelt.access(world).getEngine(); - } - - return IrisContext.get().getEngine(); - } - }, piece.getPlacementOptions(), rng, getData()); + public record ParentConnection(PlannedPiece parent, IrisJigsawPieceConnector parentConnector, PlannedPiece self, IrisJigsawPieceConnector connector) { + public IrisPosition getTargetPosition() { + var pos = parent.realPositions.get(parentConnector); + if (pos == null) return null; + return pos.add(new IrisPosition(parentConnector.getDirection().toVector())) + .sub(connector.getPosition()) + .sub(new IrisPosition(self.object.getCenter())); + } } } diff --git a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java index e4d2b0853..77d4157f7 100644 --- a/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/jigsaw/PlannedStructure.java @@ -21,20 +21,20 @@ package com.volmit.iris.engine.jigsaw; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.mantle.MantleWriter; +import com.volmit.iris.engine.framework.placer.WorldObjectPlacer; import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; -import com.volmit.iris.util.matter.slices.JigsawPieceMatter; import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; +import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; +import com.volmit.iris.util.scheduling.J; import lombok.Data; import org.bukkit.Axis; -import org.bukkit.World; -import org.bukkit.block.TileState; -import org.bukkit.block.data.BlockData; + +import java.util.function.Consumer; @Data public class PlannedStructure { @@ -75,33 +75,44 @@ public class PlannedStructure { } } - public void place(MantleWriter placer, Mantle e, Engine eng) { + public boolean place(IObjectPlacer placer, Mantle e, Engine eng) { IrisObjectPlacement options = new IrisObjectPlacement(); - options.getRotation().setEnabled(false); + options.setRotation(IrisObjectRotation.of(0,0,0)); int startHeight = pieces.get(0).getPosition().getY(); + boolean placed = false; for (PlannedPiece i : pieces) { - place(i, startHeight, options, placer, e, eng); + if (place(i, startHeight, options, placer, e, eng)) + placed = true; } + if (placed) { + Position2 chunkPos = new Position2(position.getX() >> 4, position.getZ() >> 4); + Position2 regionPos = new Position2(chunkPos.getX() >> 5, chunkPos.getZ() >> 5); + JigsawStructuresContainer slice = e.get(regionPos.getX(), 0, regionPos.getZ(), JigsawStructuresContainer.class); + if (slice == null) slice = new JigsawStructuresContainer(); + slice.add(structure, chunkPos); + e.set(regionPos.getX(), 0, regionPos.getZ(), slice); + } + return placed; } - public void place(PlannedPiece i, int startHeight, IrisObjectPlacement o, MantleWriter placer, Mantle e, Engine eng) { + public boolean place(PlannedPiece i, int startHeight, IrisObjectPlacement o, IObjectPlacer placer, Mantle e, Engine eng) { IrisObjectPlacement options = o; if (i.getPiece().getPlacementOptions() != null) { options = i.getPiece().getPlacementOptions(); options.getRotation().setEnabled(false); + options.setRotateTowardsSlope(false); + options.setWarp(new IrisGeneratorStyle(NoiseStyle.FLAT)); } else { options.setMode(i.getPiece().getPlaceMode()); } - IrisObject vo = i.getOgObject(); IrisObject v = i.getObject(); int sx = (v.getW() / 2); int sz = (v.getD() / 2); int xx = i.getPosition().getX() + sx; int zz = i.getPosition().getZ() + sz; - RNG rngf = new RNG(Cache.key(xx, zz)); int offset = i.getPosition().getY() - startHeight; int height; @@ -109,12 +120,23 @@ public class PlannedStructure { if (i.getStructure().getStructure().getOverrideYRange() != null) { height = (int) i.getStructure().getStructure().getOverrideYRange().get(rng, xx, zz, getData()); } else { - height = placer.getHighest(xx, zz, getData()); + height = placer.getHighest(xx, zz, getData(), options.isUnderwater()); } } else { height = i.getStructure().getStructure().getLockY(); } + PlannedPiece.ParentConnection connection = i.getParent(); + if (connection != null && connection.connector().isLockY()) { + var pos = connection.getTargetPosition(); + if (pos != null) { + height = pos.getY(); + offset = 0; + } else { + Iris.warn("Failed to get target position for " + v.getLoadKey()); + } + } + height += offset + (v.getH() / 2); if (options.getMode().equals(ObjectPlaceMode.PAINT)) { @@ -123,75 +145,15 @@ public class PlannedStructure { int id = rng.i(0, Integer.MAX_VALUE); JigsawPieceContainer container = JigsawPieceContainer.toContainer(i.getPiece()); - vo.place(xx, height, zz, new IObjectPlacer() { - @Override - public int getHighest(int x, int z, IrisData data) { - return placer.getHighest(x, z, data); - } - - @Override - public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) { - return placer.getHighest(x, z, data, ignoreFluid); - } - - @Override - public void set(int x, int y, int z, BlockData d) { - placer.setData(x, y, z, container); - placer.set(x, y, z, d); - } - - @Override - public BlockData get(int x, int y, int z) { - placer.setData(x, y, z, container); - return placer.get(x, y, z); - } - - @Override - public boolean isPreventingDecay() { - return placer.isPreventingDecay(); - } - - @Override - public boolean isCarved(int x, int y, int z) { - return placer.isCarved(x, y, z); - } - - @Override - public boolean isSolid(int x, int y, int z) { - return placer.isSolid(x, y, z); - } - - @Override - public boolean isUnderwater(int x, int z) { - return placer.isUnderwater(x, z); - } - - @Override - public int getFluidHeight() { - return placer.getFluidHeight(); - } - - @Override - public boolean isDebugSmartBore() { - return placer.isDebugSmartBore(); - } - - @Override - public void setTile(int xx, int yy, int zz, TileData tile) { - placer.setTile(xx, yy, zz, tile); - } - - @Override - public Engine getEngine() { - return placer.getEngine(); - } - }, options, rng, (b, data) -> e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id), null, getData()); + i.setRealPositions(xx, height, zz, placer); + return v.place(xx, height, zz, placer, options, rng, (b, data) -> { + e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id); + e.set(b.getX(), b.getY(), b.getZ(), container); + }, null, getData().getEngine() != null ? getData() : eng.getData()) != -1; } - public void place(World world) { - for (PlannedPiece i : pieces) { - Iris.sq(() -> i.place(world)); - } + public void place(WorldObjectPlacer placer, Consumer consumer) { + J.s(() -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine()))); } private void generateOutwards() { @@ -227,9 +189,7 @@ public class PlannedStructure { private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea) { if (!piece.getPiece().getPlacementOptions().getRotation().isEnabled()) { - if (generateRotatedPiece(piece, pieceConnector, idea, 0, 0, 0)) { - return true; - } + return generateRotatedPiece(piece, pieceConnector, idea, 0, 0, 0); } KList forder1 = new KList().qadd(0).qadd(1).qadd(2).qadd(3).shuffle(rng); @@ -276,7 +236,7 @@ public class PlannedStructure { } private boolean generateRotatedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, IrisJigsawPiece idea, int x, int y, int z) { - return generateRotatedPiece(piece, pieceConnector, idea, IrisObjectRotation.of(x, y, z)); + return generateRotatedPiece(piece, pieceConnector, idea, IrisObjectRotation.of(x * 90D, y * 90D, z * 90D)); } private boolean generatePositionedPiece(PlannedPiece piece, IrisJigsawPieceConnector pieceConnector, PlannedPiece test, IrisJigsawPieceConnector testConnector) { @@ -300,8 +260,7 @@ public class PlannedStructure { return false; } - piece.connect(pieceConnector); - test.connect(testConnector); + piece.connect(pieceConnector, test, testConnector); pieces.add(test); return true; @@ -310,7 +269,8 @@ public class PlannedStructure { private KList getShuffledPiecesFor(IrisJigsawPieceConnector c) { KList p = new KList<>(); - for (String i : c.getPools().shuffleCopy(rng)) { + KList pools = terminating && getStructure().getTerminatePool() != null ? new KList<>(getStructure().getTerminatePool()) : c.getPools().shuffleCopy(rng); + for (String i : pools) { for (String j : getData().getJigsawPoolLoader().load(i).getPieces().shuffleCopy(rng)) { IrisJigsawPiece pi = getData().getJigsawPieceLoader().load(j); @@ -336,7 +296,9 @@ public class PlannedStructure { } public KList getPiecesWithAvailableConnectors() { - return pieces.copy().removeWhere(PlannedPiece::isFull); + KList available = pieces.copy().removeWhere(PlannedPiece::isFull); + if (!terminating) available.removeIf(PlannedPiece::isDead); + return available; } public int getVolume() { 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 25aca5aed..187b41685 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 @@ -23,6 +23,7 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.engine.IrisComplex; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineTarget; +import com.volmit.iris.engine.framework.SeedManager; import com.volmit.iris.engine.mantle.components.MantleJigsawComponent; import com.volmit.iris.engine.mantle.components.MantleObjectComponent; import com.volmit.iris.engine.object.IObjectPlacer; @@ -298,7 +299,7 @@ public interface EngineMantle extends IObjectPlacer { default long getToUnload(){ return getMantle().getToUnload().size(); } - default long getNotClearedLoadedRegions(){ + default long getNotQueuedLoadedRegions(){ return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size(); } default double getTectonicDuration(){ 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 2aa090e00..b7d8223b7 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 @@ -23,13 +23,18 @@ import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.MantleWriter; 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.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.math.Position2; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import com.volmit.iris.util.noise.CNG; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -38,22 +43,21 @@ public class MantleJigsawComponent extends IrisMantleComponent { public MantleJigsawComponent(EngineMantle engineMantle) { super(engineMantle, MantleFlag.JIGSAW); - cng = NoiseStyle.STATIC.create(new RNG(engineMantle.getEngine().getSeedManager().getJigsaw())); + cng = NoiseStyle.STATIC.create(new RNG(jigsaw())); } @Override public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) { - RNG rng = new RNG(cng.fit(-Integer.MAX_VALUE, Integer.MAX_VALUE, x, z)); int xxx = 8 + (x << 4); int zzz = 8 + (z << 4); IrisRegion region = getComplex().getRegionStream().get(xxx, zzz); IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz); - generateJigsaw(writer, rng, x, z, biome, region); + generateJigsaw(writer, x, z, biome, region); } @ChunkCoordinates - private void generateJigsaw(MantleWriter writer, RNG rng, int x, int z, IrisBiome biome, IrisRegion region) { - boolean placed = false; + private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) { + long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z); if (getDimension().getStronghold() != null) { List poss = getDimension().getStrongholds(seed()); @@ -62,51 +66,71 @@ public class MantleJigsawComponent extends IrisMantleComponent { for (Position2 pos : poss) { if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) { IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getDimension().getStronghold()); - place(writer, pos.toIris(), structure, rng); - placed = true; + place(writer, pos.toIris(), structure, new RNG(seed)); + return; } } } } - if (!placed) { - for (IrisJigsawStructurePlacement i : biome.getJigsawStructures()) { - if (rng.nextInt(i.getRarity()) == 0) { - IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15)); - IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure()); - place(writer, position, structure, rng); - placed = true; - } - } - } + KSet cachedRegions = new KSet<>(); + KMap> cache = new KMap<>(); + KMap distanceCache = new KMap<>(); + boolean placed = placeStructures(writer, seed, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache); + if (!placed) + placed = placeStructures(writer, seed, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache); + if (!placed) + placeStructures(writer, seed, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache); + } - if (!placed) { - for (IrisJigsawStructurePlacement i : region.getJigsawStructures()) { - if (rng.nextInt(i.getRarity()) == 0) { - IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15)); - IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure()); - place(writer, position, structure, rng); - placed = true; - } - } - } + @ChunkCoordinates + private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList structures, + KSet cachedRegions, KMap> cache, KMap distanceCache) { + IrisJigsawStructurePlacement i = pick(structures, seed, x, z); + if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache)) + return false; + RNG rng = new RNG(seed); + IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15)); + IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure()); + return place(writer, position, structure, rng); + } - if (!placed) { - for (IrisJigsawStructurePlacement i : getDimension().getJigsawStructures()) { - if (rng.nextInt(i.getRarity()) == 0) { - IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15)); - IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure()); - place(writer, position, structure, rng); + @ChunkCoordinates + private boolean checkMinDistances(KMap minDistances, int x, int z, KSet cachedRegions, KMap> cache, KMap distanceCache) { + int range = 0; + for (int d : minDistances.values()) + range = Math.max(range, d); + + for (int xx = -range; xx <= range; xx++) { + for (int zz = -range; zz <= range; zz++) { + Position2 pos = new Position2((xx + x) >> 5, (zz + z) >> 5); + if (cachedRegions.contains(pos)) continue; + cachedRegions.add(pos); + JigsawStructuresContainer container = getMantle().get(pos.getX(), 0, pos.getZ(), JigsawStructuresContainer.class); + if (container == null) continue; + for (String key : container.getStructures()) { + cache.computeIfAbsent(key, k -> new KSet<>()).addAll(container.getPositions(key)); } } } + Position2 pos = new Position2(x, z); + for (String structure : minDistances.keySet()) { + if (!cache.containsKey(structure)) continue; + double minDist = minDistances.get(structure); + minDist = minDist * minDist; + for (Position2 sPos : cache.get(structure)) { + double dist = distanceCache.computeIfAbsent(sPos, position2 -> position2.distance(pos)); + if (minDist > dist) return true; + } + } + return false; } @ChunkCoordinates public IrisJigsawStructure guess(int x, int z) { // todo The guess doesnt bring into account that the placer may return -1 - boolean t = false; - RNG rng = new RNG(cng.fit(-Integer.MAX_VALUE, Integer.MAX_VALUE, x, z)); + // todo doesnt bring skipped placements into account + long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z); IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8); IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8); @@ -122,29 +146,26 @@ public class MantleJigsawComponent extends IrisMantleComponent { } } - for (IrisJigsawStructurePlacement i : biome.getJigsawStructures()) { - if (rng.nextInt(i.getRarity()) == 0) { - return getData().getJigsawStructureLoader().load(i.getStructure()); - } - } + IrisJigsawStructurePlacement i = pick(biome.getJigsawStructures(), seed, x, z); + if (i == null) i = pick(region.getJigsawStructures(), seed, x, z); + if (i == null) i = pick(getDimension().getJigsawStructures(), seed, x, z); + return i != null ? getData().getJigsawStructureLoader().load(i.getStructure()) : null; + } - for (IrisJigsawStructurePlacement i : region.getJigsawStructures()) { - if (rng.nextInt(i.getRarity()) == 0) { - return getData().getJigsawStructureLoader().load(i.getStructure()); - } - } - - for (IrisJigsawStructurePlacement i : getDimension().getJigsawStructures()) { - if (rng.nextInt(i.getRarity()) == 0) { - return getData().getJigsawStructureLoader().load(i.getStructure()); - } - } - - return null; + @Nullable + @ChunkCoordinates + private IrisJigsawStructurePlacement pick(List structures, long seed, int x, int z) { + return IRare.pick(structures.stream() + .filter(p -> p.shouldPlace(jigsaw(), x, z)) + .toList(), new RNG(seed).nextDouble()); } @BlockCoordinates - private void place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) { - new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine()); + private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) { + return new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine()); + } + + private long jigsaw() { + return getEngineMantle().getEngine().getSeedManager().getJigsaw(); } } 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 c08d3fa11..d75dca7ad 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 @@ -194,13 +194,14 @@ public class IrisBiome extends IrisRegistrant implements IRare { return getCustomDerivitives() != null && getCustomDerivitives().isNotEmpty(); } - public double getGenLinkMax(String loadKey) { + public double getGenLinkMax(String loadKey, Engine engine) { Integer v = genCacheMax.aquire(() -> { KMap l = new KMap<>(); for (IrisBiomeGeneratorLink i : getGenerators()) { - l.put(i.getGenerator(), i.getMax()); + l.put(i.getGenerator(), i.getMax(engine)); + } return l; @@ -209,13 +210,13 @@ public class IrisBiome extends IrisRegistrant implements IRare { return v == null ? 0 : v; } - public double getGenLinkMin(String loadKey) { + public double getGenLinkMin(String loadKey, Engine engine) { Integer v = genCacheMin.aquire(() -> { KMap l = new KMap<>(); for (IrisBiomeGeneratorLink i : getGenerators()) { - l.put(i.getGenerator(), i.getMin()); + l.put(i.getGenerator(), i.getMin(engine)); } return l; @@ -450,26 +451,26 @@ public class IrisBiome extends IrisRegistrant implements IRare { return real; } - public int getMaxHeight() { + public int getMaxHeight(Engine engine) { return maxHeight.aquire(() -> { int maxHeight = 0; for (IrisBiomeGeneratorLink i : getGenerators()) { - maxHeight += i.getMax(); + maxHeight += i.getMax(engine); } return maxHeight; }); } - public int getMaxWithObjectHeight(IrisData data) { + public int getMaxWithObjectHeight(IrisData data, Engine engine) { return maxWithObjectHeight.aquire(() -> { int maxHeight = 0; for (IrisBiomeGeneratorLink i : getGenerators()) { - maxHeight += i.getMax(); + maxHeight += i.getMax(engine); } int gg = 0; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeCustom.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeCustom.java index 2b7052db4..dd3e16af6 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeCustom.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeCustom.java @@ -19,6 +19,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -68,8 +69,10 @@ public class IrisBiomeCustom { @Desc("The biome's category type") private IrisBiomeCustomCategory category = IrisBiomeCustomCategory.plains; + @MinNumber(0) + @MaxNumber(20) @Desc("The spawn rarity of any defined spawners") - private int spawnRarity = -1; + private int spawnRarity = 0; @Desc("The color of the sky, top half of sky. (hex format)") private String skyColor = "#79a8e1"; @@ -89,7 +92,7 @@ public class IrisBiomeCustom { @Desc("The color of foliage (hex format). Leave blank / don't define to not change") private String foliageColor = ""; - public String generateJson() { + public String generateJson(IDataFixer fixer) { JSONObject effects = new JSONObject(); effects.put("sky_color", parseColor(getSkyColor())); effects.put("fog_color", parseColor(getFogColor())); @@ -155,7 +158,7 @@ public class IrisBiomeCustom { j.put("spawners", spawners); } - return j.toString(4); + return fixer.fixCustomBiome(this, j).toString(4); } private int parseColor(String c) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeGeneratorLink.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeGeneratorLink.java index 177283f74..00c2e4fac 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeGeneratorLink.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBiomeGeneratorLink.java @@ -18,13 +18,13 @@ package com.volmit.iris.engine.object; +import com.volmit.iris.Iris; import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.data.DataProvider; import com.volmit.iris.util.interpolation.IrisInterpolation; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.experimental.Accessors; @Snippet("generator-layer") @@ -39,22 +39,23 @@ public class IrisBiomeGeneratorLink { @RegistryListResource(IrisGenerator.class) @Desc("The generator id") private String generator = "default"; - @DependsOn({"min", "max"}) + @DependsOn({ "min", "max" }) @Required @MinNumber(-2032) // TODO: WARNING HEIGHT @MaxNumber(2032) // TODO: WARNING HEIGHT @Desc("The min block value (value + fluidHeight)") + @Getter(AccessLevel.NONE) private int min = 0; - @DependsOn({"min", "max"}) + @DependsOn({ "min", "max" }) @Required @MinNumber(-2032) // TODO: WARNING HEIGHT @MaxNumber(2032) // TODO: WARNING HEIGHT + @Getter(AccessLevel.NONE) @Desc("The max block value (value + fluidHeight)") private int max = 0; public IrisGenerator getCachedGenerator(DataProvider g) { - return gen.aquire(() -> - { + return gen.aquire(() -> { IrisGenerator gen = g.getData().getGeneratorLoader().load(getGenerator()); if (gen == null) { @@ -65,6 +66,70 @@ public class IrisBiomeGeneratorLink { }); } + private int[] getBiomeGeneratorsRaw(Engine engine) { + int max = engine.getDimension().getMinHeight(); + int min = engine.getDimension().getMaxHeight(); + for (IrisBiome biome : engine.getAllBiomes()) { + for (IrisBiomeGeneratorLink i : biome.getGenerators()) { + int biomeRawMax = i.getMaxRaw(); + int biomeRawMin = i.getMinRaw(); + if (max < biomeRawMax) + max = biomeRawMax; + if (min > biomeRawMin) + min = biomeRawMin; + } + } + + return new int[] { min, max }; + } + + private int calculateHeight(Engine engine, int option) { + int dmx = engine.getDimension().getMaxHeight(); + int dmn = engine.getDimension().getMinHeight(); + int[] heights = getBiomeGeneratorsRaw(engine); + int gmx = heights[1]; + int gmn = heights[0]; + + int mx = getMaxRaw(); + int mn = getMinRaw(); + if (engine.getDimension().isSmartVanillaHeight()) { + if (mx > 0) + mx = Math.min((int) (((float) mx / (float) gmx) * 300.0f), 300); + if (mx < 0) + mx = Math.min((int) (((float) mx / (float) gmn) * 300.0f), 56); + + if (mn > 0) + mn = Math.min((int) (((float) mn / (float) gmx) * 300.0f), 300); + if (mn < 0) + mn = Math.min((int) (((float) mn / (float) gmn) * 300.0f), 56); + } + + if (option == 1) { + return mx; + } + if (option == 0) { + return mn; + } + Iris.error("Fatal Generator error!"); + return 0; + } + + public int getMax(Engine engine) { + return calculateHeight(engine, 1); + } + + public int getMin(Engine engine) { + return calculateHeight(engine, 0); + } + + private int getMaxRaw() { + return max; + } + + private int getMinRaw() { + return min; + } + public double getHeight(DataProvider xg, double x, double z, long seed) { double g = getCachedGenerator(xg).getHeight(x, z, seed); g = g < 0 ? 0 : g; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java index 17dd4623d..acaa55904 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java @@ -82,13 +82,17 @@ public class IrisCavePlacer implements IRare { } if (y == -1) { - int h = (int) caveStartHeight.get(rng, x, z, data); - int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9); - y = Math.min(h, ma); + if(!breakSurface) { + int eH = engine.getHeight(x, z); + if (caveStartHeight.getMax() > eH) { + caveStartHeight.setMax(eH); + } + } + y = (int) caveStartHeight.get(rng, x, z, data); } try { - cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), waterHint); + cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), waterHint); } catch (Throwable e) { e.printStackTrace(); fail.set(true); 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 bb1cf5cf1..d2aa52db2 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 @@ -21,6 +21,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisRegistrant; +import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; @@ -207,6 +208,8 @@ public class IrisDimension extends IrisRegistrant { private IrisCarving carving = new IrisCarving(); @Desc("Configuration of fluid bodies such as rivers & lakes") private IrisFluidBodies fluidBodies = new IrisFluidBodies(); + @Desc("forceConvertTo320Height") + private Boolean forceConvertTo320Height = false; @Desc("The world environment") private Environment environment = Environment.NORMAL; @RegistryListResource(IrisRegion.class) @@ -228,6 +231,8 @@ public class IrisDimension extends IrisRegistrant { private IrisRange dimensionHeightEnd = new IrisRange(-64, 320); @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") private IrisRange dimensionHeightNether = new IrisRange(-64, 320); + @Desc("Enable smart vanilla height") + private boolean smartVanillaHeight = false; @RegistryListResource(IrisBiome.class) @Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.") private String focus = ""; @@ -439,7 +444,7 @@ public class IrisDimension extends IrisRegistrant { return landBiomeStyle; } - public boolean installDataPack(DataProvider data, File datapacks) { + public boolean installDataPack(IDataFixer fixer, DataProvider data, File datapacks, double ultimateMaxHeight, double ultimateMinHeight) { boolean write = false; boolean changed = false; @@ -459,7 +464,7 @@ public class IrisDimension extends IrisRegistrant { Iris.verbose(" Installing Data Pack Biome: " + output.getPath()); output.getParentFile().mkdirs(); try { - IO.writeAll(output, j.generateJson()); + IO.writeAll(output, j.generateJson(fixer)); } catch (IOException e) { Iris.reportError(e); e.printStackTrace(); @@ -470,7 +475,9 @@ public class IrisDimension extends IrisRegistrant { if (!dimensionHeight.equals(new IrisRange(-64, 320)) && this.name.equalsIgnoreCase("overworld")) { Iris.verbose(" Installing Data Pack Dimension Types: \"minecraft:overworld\", \"minecraft:the_nether\", \"minecraft:the_end\""); - changed = writeDimensionType(changed, datapacks); + dimensionHeight.setMax(ultimateMaxHeight); + dimensionHeight.setMin(ultimateMinHeight); + changed = writeDimensionType(fixer, changed, datapacks); } if (write) { @@ -509,13 +516,13 @@ public class IrisDimension extends IrisRegistrant { } - public boolean writeDimensionType(boolean changed, File datapacks) { + public boolean writeDimensionType(IDataFixer fixer, boolean changed, File datapacks) { File dimTypeOverworld = new File(datapacks, "iris/data/minecraft/dimension_type/overworld.json"); if (!dimTypeOverworld.exists()) changed = true; dimTypeOverworld.getParentFile().mkdirs(); try { - IO.writeAll(dimTypeOverworld, generateDatapackJsonOverworld()); + IO.writeAll(dimTypeOverworld, generateDatapackJsonOverworld(fixer)); } catch (IOException e) { Iris.reportError(e); e.printStackTrace(); @@ -527,7 +534,7 @@ public class IrisDimension extends IrisRegistrant { changed = true; dimTypeNether.getParentFile().mkdirs(); try { - IO.writeAll(dimTypeNether, generateDatapackJsonNether()); + IO.writeAll(dimTypeNether, generateDatapackJsonNether(fixer)); } catch (IOException e) { Iris.reportError(e); e.printStackTrace(); @@ -539,7 +546,7 @@ public class IrisDimension extends IrisRegistrant { changed = true; dimTypeEnd.getParentFile().mkdirs(); try { - IO.writeAll(dimTypeEnd, generateDatapackJsonEnd()); + IO.writeAll(dimTypeEnd, generateDatapackJsonEnd(fixer)); } catch (IOException e) { Iris.reportError(e); e.printStackTrace(); @@ -548,27 +555,27 @@ public class IrisDimension extends IrisRegistrant { return changed; } - private String generateDatapackJsonOverworld() { + private String generateDatapackJsonOverworld(IDataFixer fixer) { JSONObject obj = new JSONObject(DP_OVERWORLD_DEFAULT); obj.put("min_y", dimensionHeight.getMin()); obj.put("height", dimensionHeight.getMax() - dimensionHeight.getMin()); obj.put("logical_height", logicalHeight); - return obj.toString(4); + return fixer.fixDimension(obj).toString(4); } - private String generateDatapackJsonNether() { + private String generateDatapackJsonNether(IDataFixer fixer) { JSONObject obj = new JSONObject(DP_NETHER_DEFAULT); obj.put("min_y", dimensionHeightNether.getMin()); obj.put("height", dimensionHeightNether.getMax() - dimensionHeightNether.getMin()); obj.put("logical_height", logicalHeightNether); - return obj.toString(4); + return fixer.fixDimension(obj).toString(4); } - private String generateDatapackJsonEnd() { + private String generateDatapackJsonEnd(IDataFixer fixer) { JSONObject obj = new JSONObject(DP_END_DEFAULT); obj.put("min_y", dimensionHeightEnd.getMin()); obj.put("height", dimensionHeightEnd.getMax() - dimensionHeightEnd.getMin()); obj.put("logical_height", logicalHeightEnd); - return obj.toString(4); + return fixer.fixDimension(obj).toString(4); } } 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 8dabc091d..6f732f449 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 @@ -21,6 +21,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.loader.IrisRegistrant; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; @@ -28,6 +29,7 @@ import com.volmit.iris.util.format.C; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.misc.E; import com.volmit.iris.util.plugin.Chunks; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; @@ -41,6 +43,7 @@ import org.bukkit.*; import org.bukkit.attribute.Attributable; import org.bukkit.entity.*; import org.bukkit.entity.Panda.Gene; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.loot.LootContext; @@ -63,10 +66,14 @@ import java.util.concurrent.atomic.AtomicReference; @Data @EqualsAndHashCode(callSuper = false) public class IrisEntity extends IrisRegistrant { + private static final Particle ITEM = E.getOrDefault(Particle.class, "ITEM_CRACK", "ITEM"); @Required @Desc("The type of entity to spawn. To spawn a mythic mob, set this type to unknown and define mythic type.") private EntityType type = EntityType.UNKNOWN; + @Desc("The SpawnReason to spawn the entity with.") + private CreatureSpawnEvent.SpawnReason reason = CreatureSpawnEvent.SpawnReason.NATURAL; + @Desc("The custom name of this entity") private String customName = ""; @@ -381,7 +388,7 @@ public class IrisEntity extends IrisRegistrant { if (e.getLocation().getBlock().getType().isSolid() || ((LivingEntity) e).getEyeLocation().getBlock().getType().isSolid()) { e.teleport(start.add(new Vector(0, 0.1, 0))); ItemStack itemCrackData = new ItemStack(((LivingEntity) e).getEyeLocation().clone().subtract(0, 2, 0).getBlock().getBlockData().getMaterial()); - e.getWorld().spawnParticle(Particle.ITEM_CRACK, ((LivingEntity) e).getEyeLocation(), 6, 0.2, 0.4, 0.2, 0.06f, itemCrackData); + e.getWorld().spawnParticle(ITEM, ((LivingEntity) e).getEyeLocation(), 6, 0.2, 0.4, 0.2, 0.06f, itemCrackData); if (M.r(0.2)) { e.getWorld().playSound(e.getLocation(), Sound.BLOCK_CHORUS_FLOWER_GROW, 0.8f, 0.1f); } @@ -457,7 +464,7 @@ public class IrisEntity extends IrisRegistrant { } - return at.getWorld().spawnEntity(at, getType()); + return INMS.get().spawnEntity(at, getType(), getReason()); } public boolean isCitizens() { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java index 9b2aa6fb0..a507c342f 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java @@ -19,21 +19,28 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.format.C; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterMarker; import com.volmit.iris.util.matter.slices.MarkerMatter; +import io.lumine.mythic.bukkit.adapters.BukkitEntity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.BoundingBox; @Snippet("entity-spawn") @Accessors(chain = true) @@ -165,12 +172,19 @@ public class IrisEntitySpawn implements IRare { return null; } - Entity e = irisEntity.spawn(g, at.add(0.5, 0, 0.5), rng.aquire(() -> new RNG(g.getSeedManager().getEntity()))); + Vector3d boundingBox = INMS.get().getBoundingbox(irisEntity.getType()); + if (!ignoreSurfaces && boundingBox != null) { + boolean isClearForSpawn = isAreaClearForSpawn(at, boundingBox); + if (!isClearForSpawn) { + return null; + } + } + + Entity e = irisEntity.spawn(g, at.add(0.5, 0.5, 0.5), rng.aquire(() -> new RNG(g.getSeedManager().getEntity()))); if (e != null) { Iris.debug("Spawned " + C.DARK_AQUA + "Entity<" + getEntity() + "> " + C.GREEN + e.getType() + C.LIGHT_PURPLE + " @ " + C.GRAY + e.getLocation().getX() + ", " + e.getLocation().getY() + ", " + e.getLocation().getZ()); } - return e; } catch (Throwable e) { Iris.reportError(e); @@ -179,4 +193,25 @@ public class IrisEntitySpawn implements IRare { return null; } } + + private boolean isAreaClearForSpawn(Location center, Vector3d boundingBox) { + World world = center.getWorld(); + int startX = center.getBlockX() - (int) (boundingBox.x / 2); + int endX = center.getBlockX() + (int) (boundingBox.x / 2); + int startY = center.getBlockY(); + int endY = center.getBlockY() + (int) boundingBox.y; + int startZ = center.getBlockZ() - (int) (boundingBox.z / 2); + int endZ = center.getBlockZ() + (int) (boundingBox.z / 2); + + for (int x = startX; x <= endX; x++) { + for (int y = startY; y <= endY; y++) { + for (int z = startZ; z <= endZ; z++) { + if (world.getBlockAt(x, y, z).getType() != Material.AIR) { + return false; + } + } + } + } + return true; + } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawMinDistance.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawMinDistance.java new file mode 100644 index 000000000..818f4b3dd --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawMinDistance.java @@ -0,0 +1,29 @@ +package com.volmit.iris.engine.object; + +import com.volmit.iris.engine.object.annotations.Desc; +import com.volmit.iris.engine.object.annotations.MinNumber; +import com.volmit.iris.engine.object.annotations.RegistryListResource; +import com.volmit.iris.engine.object.annotations.Required; +import com.volmit.iris.engine.object.annotations.Snippet; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Snippet("jigsaw-structure-min-distance") +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Desc("Represents the min distance between jigsaw structure placements") +@Data +public class IrisJigsawMinDistance { + @Required + @RegistryListResource(IrisJigsawStructure.class) + @Desc("The structure to check against") + private String structure; + + @Required + @MinNumber(0) + @Desc("The min distance in blocks to a placed structure\nWARNING: The performance impact scales exponentially!") + private int distance; +} 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 72feb927c..c69aca0e4 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 @@ -101,19 +101,12 @@ public class IrisJigsawPiece extends IrisRegistrant { } public IrisJigsawPiece copy() { - IrisJigsawPiece p = new IrisJigsawPiece(); - p.setObject(getObject()); - p.setLoader(getLoader()); - p.setLoadKey(getLoadKey()); - p.setLoadFile(getLoadFile()); - p.setConnectors(new KList<>()); - p.setPlacementOptions(getPlacementOptions()); - - for (IrisJigsawPieceConnector i : getConnectors()) { - p.getConnectors().add(i.copy()); - } - - return p; + var gson = getLoader().getGson(); + IrisJigsawPiece copy = gson.fromJson(gson.toJson(this), IrisJigsawPiece.class); + copy.setLoader(getLoader()); + copy.setLoadKey(getLoadKey()); + copy.setLoadFile(getLoadFile()); + return copy; } public boolean isTerminal() { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java index bc5fce61b..f3afe074a 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPieceConnector.java @@ -79,6 +79,9 @@ public class IrisJigsawPieceConnector { @Required private IrisDirection direction = IrisDirection.UP_POSITIVE_Y; + @Desc("Lock the Y position of this connector") + private boolean lockY = false; + public String toString() { return direction.getFace().name() + "@(" + position.getX() + "," + position.getY() + "," + position.getZ() + ")"; } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java index 8a009364f..9d6d28506 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java @@ -56,6 +56,10 @@ public class IrisJigsawStructure extends IrisRegistrant { @Desc("If set to true, iris will look for any pieces with only one connector in valid pools for edge connectors and attach them to 'terminate' the paths/piece connectors. Essentially it caps off ends. For example in a village, Iris would add houses to the ends of roads where possible. For terminators to be selected, they can only have one connector or they wont be chosen.") private boolean terminate = true; + @RegistryListResource(IrisJigsawPool.class) + @Desc("The pool to use when terminating pieces") + private String terminatePool = null; + @Desc("Override the y range instead of placing on the height map") private IrisStyledRange overrideYRange = null; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java index 609658e2d..e816cfa78 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructurePlacement.java @@ -18,17 +18,24 @@ 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.MaxNumber; +import com.volmit.iris.engine.object.annotations.MinNumber; import com.volmit.iris.engine.object.annotations.RegistryListResource; import com.volmit.iris.engine.object.annotations.Required; import com.volmit.iris.engine.object.annotations.Snippet; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.math.RNG; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; - @Snippet("jigsaw-structure-placement") @Accessors(chain = true) @NoArgsConstructor @@ -36,13 +43,99 @@ import lombok.experimental.Accessors; @Desc("Represents a jigsaw structure placer") @Data @EqualsAndHashCode(callSuper = false) -public class IrisJigsawStructurePlacement { +public class IrisJigsawStructurePlacement implements IRare { @RegistryListResource(IrisJigsawStructure.class) @Required @Desc("The structure to place") private String structure; @Required - @Desc("The 1 in X chance rarity") + @Desc("The 1 in X chance rarity applies when generating multiple structures at once") private int rarity = 100; + + @Required + @Desc("The salt to use when generating the structure (to differentiate structures)") + @MinNumber(Long.MIN_VALUE) + @MaxNumber(Long.MAX_VALUE) + private long salt = 0; + + @Required + @MinNumber(0) + @Desc("Average distance in chunks between two neighboring generation attempts") + private int spacing = -1; + + @Required + @MinNumber(0) + @Desc("Minimum distance in chunks between two neighboring generation attempts\nThe maximum distance of two neighboring generation attempts is 2*spacing - separation") + private int separation = -1; + + @Desc("The method used to spread the structure") + private SpreadType spreadType = SpreadType.TRIANGULAR; + + @ArrayType(type = IrisJigsawMinDistance.class) + @Desc("List of minimum distances to check for") + private KList minDistances = new KList<>(); + + public KMap collectMinDistances() { + KMap map = new KMap<>(); + for (IrisJigsawMinDistance d : minDistances) { + map.compute(d.getStructure(), (k, v) -> v != null ? Math.min(toChunks(d.getDistance()), v) : toChunks(d.getDistance())); + } + return map; + } + + private int toChunks(int blocks) { + return (int) Math.ceil(blocks / 16d); + } + + private void calculateMissing(long seed) { + seed = seed + hashCode(); + if (salt == 0) { + salt = new RNG(seed).nextLong(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + if (separation == -1 || spacing == -1) { + separation = (int) Math.round(rarity / 20d); + spacing = new RNG(seed).nextInt(separation, separation * 2); + } + } + + @ChunkCoordinates + public boolean shouldPlace(long seed, int x, int z) { + calculateMissing(seed); + if (separation > spacing) { + separation = spacing; + Iris.warn("JigsawStructurePlacement: separation must be less than or equal to spacing"); + } + + int i = Math.floorDiv(x, spacing); + int j = Math.floorDiv(z, spacing); + RNG rng = new RNG(i * 341873128712L + j * 132897987541L + seed + salt); + + int k = spacing - separation; + int l = spreadType.apply(rng, k); + int m = spreadType.apply(rng, k); + return i * spacing + l == x && j * spacing + m == z; + } + + @Desc("Spread type") + public enum SpreadType { + @Desc("Linear spread") + LINEAR(RNG::i), + @Desc("Triangular spread") + TRIANGULAR((rng, bound) -> (rng.i(bound) + rng.i(bound)) / 2); + private final SpreadMethod method; + + SpreadType(SpreadMethod method) { + this.method = method; + } + + public int apply(RNG rng, int bound) { + return method.apply(rng, bound); + } + } + + private interface SpreadMethod { + int apply(RNG rng, int bound); + } } 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 03a306a1c..cb85b0d66 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 @@ -1,1253 +1,1285 @@ -/* - * 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.object; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.loader.IrisRegistrant; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.framework.placer.HeightmapObjectPlacer; -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.data.B; -import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.interpolation.IrisInterpolation; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.math.AxisAlignedBB; -import com.volmit.iris.util.math.BlockPosition; -import com.volmit.iris.util.math.Position2; -import com.volmit.iris.util.math.RNG; -import com.volmit.iris.util.matter.MatterMarker; -import com.volmit.iris.util.parallel.BurstExecutor; -import com.volmit.iris.util.parallel.MultiBurst; -import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.scheduling.IrisLock; -import com.volmit.iris.util.scheduling.PrecisionStopwatch; -import com.volmit.iris.util.stream.ProceduralStream; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.TileState; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.MultipleFacing; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.block.data.type.Leaves; -import org.bukkit.util.BlockVector; -import org.bukkit.util.Vector; - -import java.io.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; - -@Accessors(chain = true) -@EqualsAndHashCode(callSuper = false) -public class IrisObject extends IrisRegistrant { - protected static final Vector HALF = new Vector(0.5, 0.5, 0.5); - protected static final BlockData AIR = B.get("CAVE_AIR"); - protected static final BlockData VAIR = B.get("VOID_AIR"); - protected static final BlockData VAIR_DEBUG = B.get("COBWEB"); - protected static final BlockData[] SNOW_LAYERS = new BlockData[]{B.get("minecraft:snow[layers=1]"), B.get("minecraft:snow[layers=2]"), B.get("minecraft:snow[layers=3]"), B.get("minecraft:snow[layers=4]"), B.get("minecraft:snow[layers=5]"), B.get("minecraft:snow[layers=6]"), B.get("minecraft:snow[layers=7]"), B.get("minecraft:snow[layers=8]")}; - protected transient final IrisLock readLock = new IrisLock("read-conclock"); - @Getter - @Setter - protected transient volatile boolean smartBored = false; - @Getter - @Setter - protected transient IrisLock lock = new IrisLock("Preloadcache"); - @Setter - protected transient AtomicCache aabb = new AtomicCache<>(); - private KMap blocks; - private KMap> states; - @Getter - @Setter - private int w; - @Getter - @Setter - private int d; - @Getter - @Setter - private int h; - @Getter - @Setter - private transient BlockVector center; - - public IrisObject(int w, int h, int d) { - blocks = new KMap<>(); - states = new KMap<>(); - this.w = w; - this.h = h; - this.d = d; - center = new BlockVector(w / 2, h / 2, d / 2); - } - - public IrisObject() { - this(0, 0, 0); - } - - public static BlockVector getCenterForSize(BlockVector size) { - return new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); - } - - public static AxisAlignedBB getAABBFor(BlockVector size) { - BlockVector center = new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); - return new AxisAlignedBB(new IrisPosition(new BlockVector(0, 0, 0).subtract(center).toBlockVector()), - new IrisPosition(new BlockVector(size.getX() - 1, size.getY() - 1, size.getZ() - 1).subtract(center).toBlockVector())); - } - - @SuppressWarnings({"resource", "RedundantSuppression"}) - public static BlockVector sampleSize(File file) throws IOException { - FileInputStream in = new FileInputStream(file); - DataInputStream din = new DataInputStream(in); - BlockVector bv = new BlockVector(din.readInt(), din.readInt(), din.readInt()); - Iris.later(din::close); - return bv; - } - - private static List blocksBetweenTwoPoints(Vector loc1, Vector loc2) { - List locations = new ArrayList<>(); - int topBlockX = Math.max(loc1.getBlockX(), loc2.getBlockX()); - int bottomBlockX = Math.min(loc1.getBlockX(), loc2.getBlockX()); - int topBlockY = Math.max(loc1.getBlockY(), loc2.getBlockY()); - int bottomBlockY = Math.min(loc1.getBlockY(), loc2.getBlockY()); - int topBlockZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ()); - int bottomBlockZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ()); - - for (int x = bottomBlockX; x <= topBlockX; x++) { - for (int z = bottomBlockZ; z <= topBlockZ; z++) { - for (int y = bottomBlockY; y <= topBlockY; y++) { - locations.add(new BlockVector(x, y, z)); - } - } - } - return locations; - } - - public AxisAlignedBB getAABB() { - return aabb.aquire(() -> getAABBFor(new BlockVector(w, h, d))); - } - - public void ensureSmartBored(boolean debug) { - if (smartBored) { - return; - } - - PrecisionStopwatch p = PrecisionStopwatch.start(); - BlockData vair = debug ? VAIR_DEBUG : VAIR; - lock.lock(); - AtomicInteger applied = new AtomicInteger(); - if (getBlocks().isEmpty()) { - lock.unlock(); - Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it."); - smartBored = true; - return; - } - - BlockVector max = new BlockVector(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); - BlockVector min = new BlockVector(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); - - for (BlockVector i : getBlocks().keySet()) { - max.setX(Math.max(i.getX(), max.getX())); - min.setX(Math.min(i.getX(), min.getX())); - max.setY(Math.max(i.getY(), max.getY())); - min.setY(Math.min(i.getY(), min.getY())); - max.setZ(Math.max(i.getZ(), max.getZ())); - min.setZ(Math.min(i.getZ(), min.getZ())); - } - - BurstExecutor burst = MultiBurst.burst.burst(); - - // Smash X - for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { - int finalRayY = rayY; - burst.queue(() -> { - for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - - for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) { - if (getBlocks().containsKey(new BlockVector(ray, finalRayY, rayZ))) { - start = Math.min(ray, start); - end = Math.max(ray, end); - } - } - - if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { - for (int i = start; i <= end; i++) { - BlockVector v = new BlockVector(i, finalRayY, rayZ); - - if (!B.isAir(getBlocks().get(v))) { - getBlocks().computeIfAbsent(v, (vv) -> vair); - applied.getAndIncrement(); - } - } - } - } - }); - } - - // Smash Y - for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { - int finalRayX = rayX; - burst.queue(() -> { - for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - - for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) { - if (getBlocks().containsKey(new BlockVector(finalRayX, ray, rayZ))) { - start = Math.min(ray, start); - end = Math.max(ray, end); - } - } - - if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { - for (int i = start; i <= end; i++) { - BlockVector v = new BlockVector(finalRayX, i, rayZ); - - if (!B.isAir(getBlocks().get(v))) { - getBlocks().computeIfAbsent(v, (vv) -> vair); - applied.getAndIncrement(); - } - } - } - } - }); - } - - // Smash Z - for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { - int finalRayX = rayX; - burst.queue(() -> { - for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { - int start = Integer.MAX_VALUE; - int end = Integer.MIN_VALUE; - - for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) { - if (getBlocks().containsKey(new BlockVector(finalRayX, rayY, ray))) { - start = Math.min(ray, start); - end = Math.max(ray, end); - } - } - - if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { - for (int i = start; i <= end; i++) { - BlockVector v = new BlockVector(finalRayX, rayY, i); - - if (!B.isAir(getBlocks().get(v))) { - getBlocks().computeIfAbsent(v, (vv) -> vair); - applied.getAndIncrement(); - } - } - } - } - }); - } - - burst.complete(); - smartBored = true; - lock.unlock(); - Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")"); - } - - public synchronized IrisObject copy() { - IrisObject o = new IrisObject(w, h, d); - o.setLoadKey(o.getLoadKey()); - o.setCenter(getCenter().clone()); - - for (BlockVector i : getBlocks().keySet()) { - o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone()); - } - - for (BlockVector i : getStates().keySet()) { - o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone()); - } - - return o; - } - - public void readLegacy(InputStream in) throws IOException { - DataInputStream din = new DataInputStream(in); - this.w = din.readInt(); - this.h = din.readInt(); - this.d = din.readInt(); - center = new BlockVector(w / 2, h / 2, d / 2); - int s = din.readInt(); - - for (int i = 0; i < s; i++) { - getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF())); - } - - try { - int size = din.readInt(); - - for (int i = 0; i < size; i++) { - getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); - } - } catch (Throwable e) { - Iris.reportError(e); - - } - } - - public void read(InputStream in) throws Throwable { - DataInputStream din = new DataInputStream(in); - this.w = din.readInt(); - this.h = din.readInt(); - this.d = din.readInt(); - if (!din.readUTF().equals("Iris V2 IOB;")) { - return; - } - center = new BlockVector(w / 2, h / 2, d / 2); - int s = din.readShort(); - int i; - KList palette = new KList<>(); - - for (i = 0; i < s; i++) { - palette.add(din.readUTF()); - } - - s = din.readInt(); - - for (i = 0; i < s; i++) { - getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort()))); - } - - s = din.readInt(); - - for (i = 0; i < s; i++) { - getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); - } - } - - public void write(OutputStream o) throws IOException { - DataOutputStream dos = new DataOutputStream(o); - dos.writeInt(w); - dos.writeInt(h); - dos.writeInt(d); - dos.writeUTF("Iris V2 IOB;"); - KList palette = new KList<>(); - - for (BlockData i : getBlocks().values()) { - palette.addIfMissing(i.getAsString()); - } - - dos.writeShort(palette.size()); - - for (String i : palette) { - dos.writeUTF(i); - } - - dos.writeInt(getBlocks().size()); - - for (BlockVector i : getBlocks().keySet()) { - dos.writeShort(i.getBlockX()); - dos.writeShort(i.getBlockY()); - dos.writeShort(i.getBlockZ()); - dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString())); - } - - dos.writeInt(getStates().size()); - for (BlockVector i : getStates().keySet()) { - dos.writeShort(i.getBlockX()); - dos.writeShort(i.getBlockY()); - dos.writeShort(i.getBlockZ()); - getStates().get(i).toBinary(dos); - } - } - - public void read(File file) throws IOException { - FileInputStream fin = new FileInputStream(file); - try { - read(fin); - fin.close(); - } catch (Throwable e) { - Iris.reportError(e); - fin.close(); - fin = new FileInputStream(file); - readLegacy(fin); - fin.close(); - } - } - - public void write(File file) throws IOException { - if (file == null) { - return; - } - - FileOutputStream out = new FileOutputStream(file); - write(out); - out.close(); - } - - public void shrinkwrap() { - BlockVector min = new BlockVector(); - BlockVector max = new BlockVector(); - - for (BlockVector i : getBlocks().keySet()) { - min.setX(Math.min(min.getX(), i.getX())); - min.setY(Math.min(min.getY(), i.getY())); - min.setZ(Math.min(min.getZ(), i.getZ())); - max.setX(Math.max(max.getX(), i.getX())); - max.setY(Math.max(max.getY(), i.getY())); - max.setZ(Math.max(max.getZ(), i.getZ())); - } - - w = max.getBlockX() - min.getBlockX(); - h = max.getBlockY() - min.getBlockY(); - d = max.getBlockZ() - min.getBlockZ(); - } - - public void clean() { - KMap d = new KMap<>(); - - for (BlockVector i : getBlocks().keySet()) { - d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); - } - - KMap> dx = new KMap<>(); - - for (BlockVector i : getBlocks().keySet()) { - d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); - } - - for (BlockVector i : getStates().keySet()) { - dx.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getStates().get(i))); - } - - blocks = d; - states = dx; - } - - public BlockVector getSigned(int x, int y, int z) { - if (x >= w || y >= h || z >= d) { - throw new RuntimeException(x + " " + y + " " + z + " exceeds limit of " + w + " " + h + " " + d); - } - - return new BlockVector(x, y, z).subtract(center).toBlockVector(); - } - - public void setUnsigned(int x, int y, int z, BlockData block) { - BlockVector v = getSigned(x, y, z); - - if (block == null) { - getBlocks().remove(v); - getStates().remove(v); - } else { - getBlocks().put(v, block); - } - } - - public void setUnsigned(int x, int y, int z, Block block) { - BlockVector v = getSigned(x, y, z); - - if (block == null) { - getBlocks().remove(v); - getStates().remove(v); - } else { - BlockData data = block.getBlockData(); - getBlocks().put(v, data); - TileData state = TileData.getTileState(block); - if (state != null) { - Iris.info("Saved State " + v); - getStates().put(v, state); - } - } - } - - public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { - return place(x, -1, z, placer, config, rng, rdata); - } - - public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, CarveResult c, IrisData rdata) { - return place(x, -1, z, placer, config, rng, null, c, rdata); - } - - public int place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { - return place(x, yv, z, placer, config, rng, null, null, rdata); - } - - public int place(Location loc, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { - return place(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), placer, config, rng, rdata); - } - - public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { - IObjectPlacer placer = (config.getHeightmap() != null) ? new HeightmapObjectPlacer(oplacer.getEngine() == null ? IrisContext.get().getEngine() : oplacer.getEngine(), rng, x, yv, z, config, oplacer) : oplacer; - - if (rdata != null) { - // Slope condition - if (!config.getSlopeCondition().isDefault() && - !config.getSlopeCondition().isValid(rdata.getEngine().getComplex().getSlopeStream().get(x, z))) { - return -1; - } - -// if (config.getCarvingSupport().supportsSurface()) { -// int y = placer.getHighest(x, z, rdata); -// if (placer.isCarved(x, y, z)) { -// return -1; -// } -// } - - // Rotation calculation - int slopeRotationY = 0; - ProceduralStream heightStream = rdata.getEngine().getComplex().getHeightStream(); - if (config.isRotateTowardsSlope()) { - // Whichever side of the rectangle that bounds the object is lowest is the 'direction' of the slope (simply said). - double hNorth = heightStream.get(x, z + ((float) d) / 2); - double hEast = heightStream.get(x + ((float) w) / 2, z); - double hSouth = heightStream.get(x, z - ((float) d) / 2); - double hWest = heightStream.get(x - ((float) w) / 2, z); - double min = Math.min(Math.min(hNorth, hEast), Math.min(hSouth, hWest)); - if (min == hNorth) { - slopeRotationY = 0; - } else if (min == hEast) { - slopeRotationY = 90; - } else if (min == hSouth) { - slopeRotationY = 180; - } else if (min == hWest) { - slopeRotationY = 270; - } - } - double newRotation = config.getRotation().getYAxis().getMin() + slopeRotationY; - config.getRotation().setYAxis(new IrisAxisRotationClamp(true, false, newRotation, newRotation, 90)); - config.getRotation().setEnabled(true); - } - - if (config.isSmartBore()) { - ensureSmartBored(placer.isDebugSmartBore()); - } - - boolean warped = !config.getWarp().isFlat(); - boolean stilting = (config.getMode().equals(ObjectPlaceMode.STILT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT) || - config.getMode() == ObjectPlaceMode.MIN_STILT || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT || - config.getMode() == ObjectPlaceMode.CENTER_STILT); - KMap heightmap = config.getSnow() > 0 ? new KMap<>() : null; - int spinx = rng.imax() / 1000; - int spiny = rng.imax() / 1000; - int spinz = rng.imax() / 1000; - int rty = config.getRotation().rotate(new BlockVector(0, getCenter().getBlockY(), 0), spinx, spiny, spinz).getBlockY(); - int ty = config.getTranslate().translate(new BlockVector(0, getCenter().getBlockY(), 0), config.getRotation(), spinx, spiny, spinz).getBlockY(); - int y = -1; - int xx, zz; - int yrand = config.getTranslate().getYRandom(); - yrand = yrand > 0 ? rng.i(0, yrand) : yrand < 0 ? rng.i(yrand, 0) : yrand; - boolean bail = false; - - if (yv < 0) { - if (config.getMode().equals(ObjectPlaceMode.CENTER_HEIGHT) || config.getMode() == ObjectPlaceMode.CENTER_STILT) { - y = (c != null ? c.getSurface() : placer.getHighest(x, z, getLoader(), config.isUnderwater())) + rty; - if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) { - bail = true; - } - } else if (config.getMode().equals(ObjectPlaceMode.MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.STILT)) { - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - for (int i = minX; i <= maxX; i++) { - for (int ii = minZ; ii <= maxZ; ii++) { - int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; - if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { - bail = true; - break; - } - if (h > y) - y = h; - } - } - } else if (config.getMode().equals(ObjectPlaceMode.FAST_MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT)) { - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - - int xRadius = (rotatedDimensions.getBlockX() / 2); - int xLength = xRadius + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zRadius = (rotatedDimensions.getBlockZ() / 2); - int zLength = zRadius + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - - for (int i = minX; i <= maxX; i += Math.abs(xRadius) + 1) { - for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) { - int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; - if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { - bail = true; - break; - } - if (h > y) - y = h; - } - } - } else if (config.getMode().equals(ObjectPlaceMode.MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.MIN_STILT) { - y = rdata.getEngine().getHeight() + 1; - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - - int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - for (int i = minX; i <= maxX; i++) { - for (int ii = minZ; ii <= maxZ; ii++) { - int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; - if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { - bail = true; - break; - } - if (h < y) { - y = h; - } - } - } - } else if (config.getMode().equals(ObjectPlaceMode.FAST_MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT) { - y = rdata.getEngine().getHeight() + 1; - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); - - int xRadius = (rotatedDimensions.getBlockX() / 2); - int xLength = xRadius + offset.getBlockX(); - int minX = Math.min(x - xLength, x + xLength); - int maxX = Math.max(x - xLength, x + xLength); - int zRadius = (rotatedDimensions.getBlockZ() / 2); - int zLength = zRadius + offset.getBlockZ(); - int minZ = Math.min(z - zLength, z + zLength); - int maxZ = Math.max(z - zLength, z + zLength); - - for (int i = minX; i <= maxX; i += Math.abs(xRadius) + 1) { - for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) { - int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; - if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { - bail = true; - break; - } - if (h < y) { - y = h; - } - } - } - } else if (config.getMode().equals(ObjectPlaceMode.PAINT)) { - y = placer.getHighest(x, z, getLoader(), config.isUnderwater()) + rty; - if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) { - bail = true; - } - } - } else { - y = yv; - if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) { - bail = true; - } - } - - if (yv >= 0 && config.isBottom()) { - y += Math.floorDiv(h, 2); - bail = placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z); - } - - if (bail) { - return -1; - } - - if (yv < 0) { - if (!config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) { - return -1; - } - } - - if (c != null && Math.max(0, h + yrand + ty) + 1 >= c.getHeight()) { - return -1; - } - - if (config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) { - return -1; - } - - if (!config.getClamp().canPlace(y + rty + ty, y - rty + ty)) { - return -1; - } - - if (!config.getAllowedCollisions().isEmpty() || !config.getForbiddenCollisions().isEmpty()) { - Engine engine = rdata.getEngine(); - String key; - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - for (int i = x - Math.floorDiv(w, 2) + (int) offset.getX(); i <= x + Math.floorDiv(w, 2) - (w % 2 == 0 ? 1 : 0) + (int) offset.getX(); i++) { - for (int j = y - Math.floorDiv(h, 2) + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { - for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { - key = engine.getObjectPlacementKey(i, j, k); - if (key != null) { - if (config.getForbiddenCollisions().contains(key) && !config.getAllowedCollisions().contains(key)) { - // Iris.debug("%s collides with %s (%s / %s / %s)", getLoadKey(), key, i, j, k); - return -1; - } - } - } - } - } - } - - if (config.isBore()) { - BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); - for (int i = x - Math.floorDiv(w, 2) + (int) offset.getX(); i <= x + Math.floorDiv(w, 2) - (w % 2 == 0 ? 1 : 0) + (int) offset.getX(); i++) { - for (int j = y - Math.floorDiv(h, 2) - config.getBoreExtendMinY() + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) + config.getBoreExtendMaxY() - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { - for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { - placer.set(i, j, k, AIR); - } - } - } - } - - int lowest = Integer.MAX_VALUE; - y += yrand; - readLock.lock(); - - KMap markers = null; - - try { - if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) { - markers = new KMap<>(); - for (IrisObjectMarker j : config.getMarkers()) { - IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker()); - - if (marker == null) { - continue; - } - - int max = j.getMaximumMarkers(); - - for (BlockVector i : getBlocks().k().shuffle()) { - if (max <= 0) { - break; - } - - BlockData data = getBlocks().get(i); - - for (BlockData k : j.getMark(rdata)) { - if (max <= 0) { - break; - } - - if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { - boolean a = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 1, 0)))); - boolean fff = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 2, 0)))); - - if (!marker.isEmptyAbove() || (a && fff)) { - markers.put(i, j.getMarker()); - max--; - } - } - } - } - } - } - - for (BlockVector g : getBlocks().keySet()) { - BlockData d; - TileData tile = null; - - try { - d = getBlocks().get(g); - tile = getStates().get(g); - } catch (Throwable e) { - Iris.reportError(e); - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)"); - d = AIR; - } - - if (d == null) { - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (null)"); - d = AIR; - } - - BlockVector i = g.clone(); - BlockData data = d.clone(); - i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); - i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); - - if (stilting && i.getBlockY() < lowest && !B.isAir(data)) { - lowest = i.getBlockY(); - } - - if (placer.isPreventingDecay() && (data) instanceof Leaves && !((Leaves) (data)).isPersistent()) { - ((Leaves) data).setPersistent(true); - } - - for (IrisObjectReplace j : config.getEdit()) { - if (rng.chance(j.getChance())) { - for (BlockData k : j.getFind(rdata)) { - if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { - BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); - - if (newData.getMaterial() == data.getMaterial()) - data = data.merge(newData); - else - data = newData; - - if (newData.getMaterial() == Material.SPAWNER) { - Optional> t = j.getReplace().getTile(rng, x, y, z, rdata); - if (t.isPresent()) { - tile = t.get(); - } - } - } - } - } - } - - data = config.getRotation().rotate(data, spinx, spiny, spinz); - xx = x + (int) Math.round(i.getX()); - - int yy = y + (int) Math.round(i.getY()); - zz = z + (int) Math.round(i.getZ()); - - if (warped) { - xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); - zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); - } - - if (yv < 0 && (config.getMode().equals(ObjectPlaceMode.PAINT)) && !B.isVineBlock(data)) { - yy = (int) Math.round(i.getY()) + Math.floorDiv(h, 2) + placer.getHighest(xx, zz, getLoader(), config.isUnderwater()); - } - - if (heightmap != null) { - Position2 pos = new Position2(xx, zz); - - if (!heightmap.containsKey(pos)) { - heightmap.put(pos, yy); - } - - if (heightmap.get(pos) < yy) { - heightmap.put(pos, yy); - } - } - - if (config.isMeld() && !placer.isSolid(xx, yy, zz)) { - continue; - } - - if ((config.isWaterloggable() || config.isUnderwater()) && yy <= placer.getFluidHeight() && data instanceof Waterlogged) { - // TODO Here - ((Waterlogged) data).setWaterlogged(true); - } - - if (B.isVineBlock(data)) { - MultipleFacing f = (MultipleFacing) data; - for (BlockFace face : f.getAllowedFaces()) { - BlockData facingBlock = placer.get(xx + face.getModX(), yy + face.getModY(), zz + face.getModZ()); - if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { - f.setFace(face, true); - } - } - } - - if (listener != null) { - listener.accept(new BlockPosition(xx, yy, zz), data); - } - - if (markers != null && markers.containsKey(g)) { - placer.getEngine().getMantle().getMantle().set(xx, yy, zz, new MatterMarker(markers.get(g))); - } - - boolean wouldReplace = B.isSolid(placer.get(xx, yy, zz)) && B.isVineBlock(data); - - if (!data.getMaterial().equals(Material.AIR) && !data.getMaterial().equals(Material.CAVE_AIR) && !wouldReplace) { - placer.set(xx, yy, zz, data); - if (tile != null) { - placer.setTile(xx, yy, zz, tile); - } - } - } - } catch (Throwable e) { - Iris.reportError(e); - } - readLock.unlock(); - - if (stilting) { - readLock.lock(); - IrisStiltSettings settings = config.getStiltSettings(); - for (BlockVector g : getBlocks().keySet()) { - BlockData d; - - if (settings == null || settings.getPalette() == null) { - try { - d = getBlocks().get(g); - } catch (Throwable e) { - Iris.reportError(e); - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)"); - d = AIR; - } - - if (d == null) { - Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt null)"); - d = AIR; - } - } else - d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata); - - - BlockVector i = g.clone(); - i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); - i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); - d = config.getRotation().rotate(d, spinx, spiny, spinz); - - if (i.getBlockY() != lowest) - continue; - - for (IrisObjectReplace j : config.getEdit()) { - if (rng.chance(j.getChance())) { - for (BlockData k : j.getFind(rdata)) { - if (j.isExact() ? k.matches(d) : k.getMaterial().equals(d.getMaterial())) { - BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); - - if (newData.getMaterial() == d.getMaterial()) { - d = d.merge(newData); - } else { - d = newData; - } - } - } - } - } - - if (d == null || B.isAir(d)) - continue; - - xx = x + (int) Math.round(i.getX()); - zz = z + (int) Math.round(i.getZ()); - - if (warped) { - xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); - zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); - } - - int highest = placer.getHighest(xx, zz, getLoader(), true); - - if ((config.isWaterloggable() || config.isUnderwater()) && highest <= placer.getFluidHeight() && d instanceof Waterlogged) - ((Waterlogged) d).setWaterlogged(true); - - if (yv >= 0 && config.isBottom()) - y += Math.floorDiv(h, 2); - - int lowerBound = highest - 1; - if (settings != null) { - lowerBound -= config.getStiltSettings().getOverStilt() - rng.i(0, config.getStiltSettings().getYRand()); - if (settings.getYMax() != 0) - lowerBound -= Math.min(config.getStiltSettings().getYMax() - (lowest + y - highest), 0); - } - for (int j = lowest + y; j > lowerBound; j--) { - if (B.isVineBlock(d)) { - MultipleFacing f = (MultipleFacing) d; - for (BlockFace face : f.getAllowedFaces()) { - BlockData facingBlock = placer.get(xx + face.getModX(), j + face.getModY(), zz + face.getModZ()); - if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { - f.setFace(face, true); - } - } - } - placer.set(xx, j, zz, d); - } - - } - - readLock.unlock(); - } - - if (heightmap != null) { - RNG rngx = rng.nextParallelRNG(3468854); - - for (Position2 i : heightmap.k()) { - int vx = i.getX(); - int vy = heightmap.get(i); - int vz = i.getZ(); - - if (config.getSnow() > 0) { - int height = rngx.i(0, (int) (config.getSnow() * 7)); - placer.set(vx, vy + 1, vz, SNOW_LAYERS[Math.max(Math.min(height, 7), 0)]); - } - } - } - - return y; - } - - public IrisObject rotateCopy(IrisObjectRotation rt) { - IrisObject copy = copy(); - copy.rotate(rt, 0, 0, 0); - return copy; - } - - public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) { - KMap d = new KMap<>(); - - for (BlockVector i : getBlocks().keySet()) { - d.put(r.rotate(i.clone(), spinx, spiny, spinz), r.rotate(getBlocks().get(i).clone(), - spinx, spiny, spinz)); - } - - KMap> dx = new KMap<>(); - - for (BlockVector i : getStates().keySet()) { - dx.put(r.rotate(i.clone(), spinx, spiny, spinz), getStates().get(i)); - } - - blocks = d; - states = dx; - } - - public void place(Location at) { - for (BlockVector i : getBlocks().keySet()) { - Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock(); - b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); - - if (getStates().containsKey(i)) { - Iris.info(Objects.requireNonNull(states.get(i)).toString()); - BlockState st = b.getState(); - Objects.requireNonNull(getStates().get(i)).toBukkitTry(st); - st.update(); - } - } - } - - public void placeCenterY(Location at) { - for (BlockVector i : getBlocks().keySet()) { - Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock(); - b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); - - if (getStates().containsKey(i)) { - Objects.requireNonNull(getStates().get(i)).toBukkitTry(b.getState()); - } - } - } - - public synchronized KMap getBlocks() { - return blocks; - } - - public synchronized KMap> getStates() { - return states; - } - - public void unplaceCenterY(Location at) { - for (BlockVector i : getBlocks().keySet()) { - at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false); - } - } - - public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) { - Vector sm1 = new Vector(scale - 1, scale - 1, scale - 1); - scale = Math.max(0.001, Math.min(50, scale)); - if (scale < 1) { - scale = scale - 0.0001; - } - - IrisPosition l1 = getAABB().max(); - IrisPosition l2 = getAABB().min(); - @SuppressWarnings({"unchecked", "rawtypes"}) HashMap placeBlock = new HashMap(); - - Vector center = getCenter(); - if (getH() == 2) { - center = center.setY(center.getBlockY() + 0.5); - } - if (getW() == 2) { - center = center.setX(center.getBlockX() + 0.5); - } - if (getD() == 2) { - center = center.setZ(center.getBlockZ() + 0.5); - } - - IrisObject oo = new IrisObject((int) Math.ceil((w * scale) + (scale * 2)), (int) Math.ceil((h * scale) + (scale * 2)), (int) Math.ceil((d * scale) + (scale * 2))); - - for (Map.Entry entry : blocks.entrySet()) { - BlockData bd = entry.getValue(); - placeBlock.put(entry.getKey().clone().add(HALF).subtract(center) - .multiply(scale).add(sm1).toBlockVector(), bd); - } - - for (Map.Entry entry : placeBlock.entrySet()) { - BlockVector v = entry.getKey(); - if (scale > 1) { - for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) { - oo.getBlocks().put(vec, entry.getValue()); - } - } else { - oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue()); - } - } - - if (scale > 1) { - switch (interpolation) { - case TRILINEAR -> oo.trilinear((int) Math.round(scale)); - case TRICUBIC -> oo.tricubic((int) Math.round(scale)); - case TRIHERMITE -> oo.trihermite((int) Math.round(scale)); - } - } - - return oo; - } - - public void trilinear(int rad) { - KMap v = getBlocks().copy(); - KMap b = new KMap<>(); - BlockVector min = getAABB().minbv(); - BlockVector max = getAABB().maxbv(); - - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - if (IrisInterpolation.getTrilinear(x, y, z, rad, (xx, yy, zz) -> { - BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); - - if (data == null || data.getMaterial().isAir()) { - return 0; - } - - return 1; - }) >= 0.5) { - b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); - } else { - b.put(new BlockVector(x, y, z), AIR); - } - } - } - } - - blocks = b; - } - - public void tricubic(int rad) { - KMap v = getBlocks().copy(); - KMap b = new KMap<>(); - BlockVector min = getAABB().minbv(); - BlockVector max = getAABB().maxbv(); - - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - if (IrisInterpolation.getTricubic(x, y, z, rad, (xx, yy, zz) -> { - BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); - - if (data == null || data.getMaterial().isAir()) { - return 0; - } - - return 1; - }) >= 0.5) { - b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); - } else { - b.put(new BlockVector(x, y, z), AIR); - } - } - } - } - - blocks = b; - } - - public void trihermite(int rad) { - trihermite(rad, 0D, 0D); - } - - public void trihermite(int rad, double tension, double bias) { - KMap v = getBlocks().copy(); - KMap b = new KMap<>(); - BlockVector min = getAABB().minbv(); - BlockVector max = getAABB().maxbv(); - - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - if (IrisInterpolation.getTrihermite(x, y, z, rad, (xx, yy, zz) -> { - BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); - - if (data == null || data.getMaterial().isAir()) { - return 0; - } - - return 1; - }, tension, bias) >= 0.5) { - b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); - } else { - b.put(new BlockVector(x, y, z), AIR); - } - } - } - } - - blocks = b; - } - - private BlockData nearestBlockData(int x, int y, int z) { - BlockVector vv = new BlockVector(x, y, z); - BlockData r = getBlocks().get(vv); - - if (r != null && !r.getMaterial().isAir()) { - return r; - } - - double d = Double.MAX_VALUE; - - for (Map.Entry entry : blocks.entrySet()) { - BlockData dat = entry.getValue(); - - if (dat.getMaterial().isAir()) { - continue; - } - - double dx = entry.getKey().distanceSquared(vv); - - if (dx < d) { - d = dx; - r = dat; - } - } - - return r; - } - - public int volume() { - return blocks.size(); - } - - @Override - public String getFolderName() { - return "objects"; - } - - @Override - public String getTypeName() { - return "Object"; - } - - @Override - public void scanForErrors(JSONObject p, VolmitSender sender) { - } -} +/* + * 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.object; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.loader.IrisRegistrant; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.PlacedObject; +import com.volmit.iris.engine.framework.placer.HeightmapObjectPlacer; +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.data.B; +import com.volmit.iris.util.data.IrisBlockData; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.interpolation.IrisInterpolation; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.math.AxisAlignedBB; +import com.volmit.iris.util.math.BlockPosition; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.matter.MatterMarker; +import com.volmit.iris.util.parallel.BurstExecutor; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.IrisLock; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import com.volmit.iris.util.stream.ProceduralStream; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.TileState; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.MultipleFacing; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.block.data.type.Leaves; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Vector; + +import java.io.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class IrisObject extends IrisRegistrant { + protected static final Vector HALF = new Vector(0.5, 0.5, 0.5); + protected static final BlockData AIR = B.get("CAVE_AIR"); + protected static final BlockData VAIR = B.get("VOID_AIR"); + protected static final BlockData VAIR_DEBUG = B.get("COBWEB"); + protected static final BlockData[] SNOW_LAYERS = new BlockData[]{B.get("minecraft:snow[layers=1]"), B.get("minecraft:snow[layers=2]"), B.get("minecraft:snow[layers=3]"), B.get("minecraft:snow[layers=4]"), B.get("minecraft:snow[layers=5]"), B.get("minecraft:snow[layers=6]"), B.get("minecraft:snow[layers=7]"), B.get("minecraft:snow[layers=8]")}; + protected transient final IrisLock readLock = new IrisLock("read-conclock"); + @Getter + @Setter + protected transient volatile boolean smartBored = false; + @Getter + @Setter + protected transient IrisLock lock = new IrisLock("Preloadcache"); + @Setter + protected transient AtomicCache aabb = new AtomicCache<>(); + private KMap blocks; + private KMap> states; + @Getter + @Setter + private int w; + @Getter + @Setter + private int d; + @Getter + @Setter + private int h; + @Getter + @Setter + private transient BlockVector center; + + public IrisObject(int w, int h, int d) { + blocks = new KMap<>(); + states = new KMap<>(); + this.w = w; + this.h = h; + this.d = d; + center = new BlockVector(w / 2, h / 2, d / 2); + } + + public IrisObject() { + this(0, 0, 0); + } + + public static BlockVector getCenterForSize(BlockVector size) { + return new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); + } + + public static AxisAlignedBB getAABBFor(BlockVector size) { + BlockVector center = new BlockVector(size.getX() / 2, size.getY() / 2, size.getZ() / 2); + return new AxisAlignedBB(new IrisPosition(new BlockVector(0, 0, 0).subtract(center).toBlockVector()), + new IrisPosition(new BlockVector(size.getX() - 1, size.getY() - 1, size.getZ() - 1).subtract(center).toBlockVector())); + } + + @SuppressWarnings({"resource", "RedundantSuppression"}) + public static BlockVector sampleSize(File file) throws IOException { + FileInputStream in = new FileInputStream(file); + DataInputStream din = new DataInputStream(in); + BlockVector bv = new BlockVector(din.readInt(), din.readInt(), din.readInt()); + Iris.later(din::close); + return bv; + } + + private static List blocksBetweenTwoPoints(Vector loc1, Vector loc2) { + List locations = new ArrayList<>(); + int topBlockX = Math.max(loc1.getBlockX(), loc2.getBlockX()); + int bottomBlockX = Math.min(loc1.getBlockX(), loc2.getBlockX()); + int topBlockY = Math.max(loc1.getBlockY(), loc2.getBlockY()); + int bottomBlockY = Math.min(loc1.getBlockY(), loc2.getBlockY()); + int topBlockZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ()); + int bottomBlockZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ()); + + for (int x = bottomBlockX; x <= topBlockX; x++) { + for (int z = bottomBlockZ; z <= topBlockZ; z++) { + for (int y = bottomBlockY; y <= topBlockY; y++) { + locations.add(new BlockVector(x, y, z)); + } + } + } + return locations; + } + + public AxisAlignedBB getAABB() { + return aabb.aquire(() -> getAABBFor(new BlockVector(w, h, d))); + } + + public void ensureSmartBored(boolean debug) { + if (smartBored) { + return; + } + + PrecisionStopwatch p = PrecisionStopwatch.start(); + BlockData vair = debug ? VAIR_DEBUG : VAIR; + lock.lock(); + AtomicInteger applied = new AtomicInteger(); + if (getBlocks().isEmpty()) { + lock.unlock(); + Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it."); + smartBored = true; + return; + } + + BlockVector max = new BlockVector(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); + BlockVector min = new BlockVector(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + + for (BlockVector i : getBlocks().keySet()) { + max.setX(Math.max(i.getX(), max.getX())); + min.setX(Math.min(i.getX(), min.getX())); + max.setY(Math.max(i.getY(), max.getY())); + min.setY(Math.min(i.getY(), min.getY())); + max.setZ(Math.max(i.getZ(), max.getZ())); + min.setZ(Math.min(i.getZ(), min.getZ())); + } + + BurstExecutor burst = MultiBurst.burst.burst(); + + // Smash X + for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { + int finalRayY = rayY; + burst.queue(() -> { + for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + + for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) { + if (getBlocks().containsKey(new BlockVector(ray, finalRayY, rayZ))) { + start = Math.min(ray, start); + end = Math.max(ray, end); + } + } + + if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { + for (int i = start; i <= end; i++) { + BlockVector v = new BlockVector(i, finalRayY, rayZ); + + if (!B.isAir(getBlocks().get(v))) { + getBlocks().computeIfAbsent(v, (vv) -> vair); + applied.getAndIncrement(); + } + } + } + } + }); + } + + // Smash Y + for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { + int finalRayX = rayX; + burst.queue(() -> { + for (int rayZ = min.getBlockZ(); rayZ <= max.getBlockZ(); rayZ++) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + + for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) { + if (getBlocks().containsKey(new BlockVector(finalRayX, ray, rayZ))) { + start = Math.min(ray, start); + end = Math.max(ray, end); + } + } + + if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { + for (int i = start; i <= end; i++) { + BlockVector v = new BlockVector(finalRayX, i, rayZ); + + if (!B.isAir(getBlocks().get(v))) { + getBlocks().computeIfAbsent(v, (vv) -> vair); + applied.getAndIncrement(); + } + } + } + } + }); + } + + // Smash Z + for (int rayX = min.getBlockX(); rayX <= max.getBlockX(); rayX++) { + int finalRayX = rayX; + burst.queue(() -> { + for (int rayY = min.getBlockY(); rayY <= max.getBlockY(); rayY++) { + int start = Integer.MAX_VALUE; + int end = Integer.MIN_VALUE; + + for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) { + if (getBlocks().containsKey(new BlockVector(finalRayX, rayY, ray))) { + start = Math.min(ray, start); + end = Math.max(ray, end); + } + } + + if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) { + for (int i = start; i <= end; i++) { + BlockVector v = new BlockVector(finalRayX, rayY, i); + + if (!B.isAir(getBlocks().get(v))) { + getBlocks().computeIfAbsent(v, (vv) -> vair); + applied.getAndIncrement(); + } + } + } + } + }); + } + + burst.complete(); + smartBored = true; + lock.unlock(); + Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")"); + } + + public synchronized IrisObject copy() { + IrisObject o = new IrisObject(w, h, d); + o.setLoadKey(o.getLoadKey()); + o.setLoader(getLoader()); + o.setLoadFile(getLoadFile()); + o.setCenter(getCenter().clone()); + + for (BlockVector i : getBlocks().keySet()) { + o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone()); + } + + for (BlockVector i : getStates().keySet()) { + o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone()); + } + + return o; + } + + public void readLegacy(InputStream in) throws IOException { + DataInputStream din = new DataInputStream(in); + this.w = din.readInt(); + this.h = din.readInt(); + this.d = din.readInt(); + center = new BlockVector(w / 2, h / 2, d / 2); + int s = din.readInt(); + + for (int i = 0; i < s; i++) { + getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF())); + } + + try { + int size = din.readInt(); + + for (int i = 0; i < size; i++) { + getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); + } + } catch (Throwable e) { + Iris.reportError(e); + + } + } + + public void read(InputStream in) throws Throwable { + DataInputStream din = new DataInputStream(in); + this.w = din.readInt(); + this.h = din.readInt(); + this.d = din.readInt(); + if (!din.readUTF().equals("Iris V2 IOB;")) { + return; + } + center = new BlockVector(w / 2, h / 2, d / 2); + int s = din.readShort(); + int i; + KList palette = new KList<>(); + + for (i = 0; i < s; i++) { + palette.add(din.readUTF()); + } + + s = din.readInt(); + + for (i = 0; i < s; i++) { + getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort()))); + } + + s = din.readInt(); + + for (i = 0; i < s; i++) { + getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); + } + } + + public void write(OutputStream o) throws IOException { + DataOutputStream dos = new DataOutputStream(o); + dos.writeInt(w); + dos.writeInt(h); + dos.writeInt(d); + dos.writeUTF("Iris V2 IOB;"); + KList palette = new KList<>(); + + for (BlockData i : getBlocks().values()) { + palette.addIfMissing(i.getAsString()); + } + + dos.writeShort(palette.size()); + + for (String i : palette) { + dos.writeUTF(i); + } + + dos.writeInt(getBlocks().size()); + + for (BlockVector i : getBlocks().keySet()) { + dos.writeShort(i.getBlockX()); + dos.writeShort(i.getBlockY()); + dos.writeShort(i.getBlockZ()); + dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString())); + } + + dos.writeInt(getStates().size()); + for (BlockVector i : getStates().keySet()) { + dos.writeShort(i.getBlockX()); + dos.writeShort(i.getBlockY()); + dos.writeShort(i.getBlockZ()); + getStates().get(i).toBinary(dos); + } + } + + public void read(File file) throws IOException { + FileInputStream fin = new FileInputStream(file); + try { + read(fin); + fin.close(); + } catch (Throwable e) { + Iris.reportError(e); + fin.close(); + fin = new FileInputStream(file); + readLegacy(fin); + fin.close(); + } + } + + public void write(File file) throws IOException { + if (file == null) { + return; + } + + FileOutputStream out = new FileOutputStream(file); + write(out); + out.close(); + } + + public void shrinkwrap() { + BlockVector min = new BlockVector(); + BlockVector max = new BlockVector(); + + for (BlockVector i : getBlocks().keySet()) { + min.setX(Math.min(min.getX(), i.getX())); + min.setY(Math.min(min.getY(), i.getY())); + min.setZ(Math.min(min.getZ(), i.getZ())); + max.setX(Math.max(max.getX(), i.getX())); + max.setY(Math.max(max.getY(), i.getY())); + 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); + center = new BlockVector(w / 2, h / 2, d / 2); + } + + public void clean() { + KMap d = new KMap<>(); + + for (BlockVector i : getBlocks().keySet()) { + d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); + } + + KMap> dx = new KMap<>(); + + for (BlockVector i : getBlocks().keySet()) { + d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); + } + + for (BlockVector i : getStates().keySet()) { + dx.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getStates().get(i))); + } + + blocks = d; + states = dx; + } + + public BlockVector getSigned(int x, int y, int z) { + if (x >= w || y >= h || z >= d) { + throw new RuntimeException(x + " " + y + " " + z + " exceeds limit of " + w + " " + h + " " + d); + } + + return new BlockVector(x, y, z).subtract(center).toBlockVector(); + } + + public void setUnsigned(int x, int y, int z, BlockData block) { + BlockVector v = getSigned(x, y, z); + + if (block == null) { + getBlocks().remove(v); + getStates().remove(v); + } else { + getBlocks().put(v, block); + } + } + + public void setUnsigned(int x, int y, int z, Block block) { + BlockVector v = getSigned(x, y, z); + + if (block == null) { + getBlocks().remove(v); + getStates().remove(v); + } else { + BlockData data = block.getBlockData(); + getBlocks().put(v, data); + TileData state = TileData.getTileState(block); + if (state != null) { + Iris.info("Saved State " + v); + getStates().put(v, state); + } + } + } + + public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { + return place(x, -1, z, placer, config, rng, rdata); + } + + public int place(int x, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, CarveResult c, IrisData rdata) { + return place(x, -1, z, placer, config, rng, null, c, rdata); + } + + public int place(int x, int yv, int z, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { + return place(x, yv, z, placer, config, rng, null, null, rdata); + } + + public int place(Location loc, IObjectPlacer placer, IrisObjectPlacement config, RNG rng, IrisData rdata) { + return place(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), placer, config, rng, rdata); + } + + public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { + IObjectPlacer placer = (config.getHeightmap() != null) ? new HeightmapObjectPlacer(oplacer.getEngine() == null ? IrisContext.get().getEngine() : oplacer.getEngine(), rng, x, yv, z, config, oplacer) : oplacer; + + if (rdata != null) { + // Slope condition + if (!config.getSlopeCondition().isDefault() && + !config.getSlopeCondition().isValid(rdata.getEngine().getComplex().getSlopeStream().get(x, z)) && !config.isForcePlace()) { + return -1; + } + +// if (config.getCarvingSupport().supportsSurface()) { +// int y = placer.getHighest(x, z, rdata); +// if (placer.isCarved(x, y, z)) { +// return -1; +// } +// } + + // Rotation calculation + int slopeRotationY = 0; + ProceduralStream heightStream = rdata.getEngine().getComplex().getHeightStream(); + if (config.isRotateTowardsSlope()) { + // Whichever side of the rectangle that bounds the object is lowest is the 'direction' of the slope (simply said). + double hNorth = heightStream.get(x, z + ((float) d) / 2); + double hEast = heightStream.get(x + ((float) w) / 2, z); + double hSouth = heightStream.get(x, z - ((float) d) / 2); + double hWest = heightStream.get(x - ((float) w) / 2, z); + double min = Math.min(Math.min(hNorth, hEast), Math.min(hSouth, hWest)); + if (min == hNorth) { + slopeRotationY = 0; + } else if (min == hEast) { + slopeRotationY = 90; + } else if (min == hSouth) { + slopeRotationY = 180; + } else if (min == hWest) { + slopeRotationY = 270; + } + + double newRotation = config.getRotation().getYAxis().getMin() + slopeRotationY; + if (newRotation == 0) { + config.getRotation().setYAxis(new IrisAxisRotationClamp(false, false, 0, 0, 90)); + config.getRotation().setEnabled(config.getRotation().canRotateX() || config.getRotation().canRotateZ()); + } else { + config.getRotation().setYAxis(new IrisAxisRotationClamp(true, false, newRotation, newRotation, 90)); + config.getRotation().setEnabled(true); + } + } + } + + if (config.isSmartBore()) { + ensureSmartBored(placer.isDebugSmartBore()); + } + + boolean warped = !config.getWarp().isFlat(); + boolean stilting = (config.getMode().equals(ObjectPlaceMode.STILT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT) || + config.getMode() == ObjectPlaceMode.MIN_STILT || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT || + config.getMode() == ObjectPlaceMode.CENTER_STILT); + KMap heightmap = config.getSnow() > 0 ? new KMap<>() : null; + int spinx = rng.imax() / 1000; + int spiny = rng.imax() / 1000; + int spinz = rng.imax() / 1000; + int rty = config.getRotation().rotate(new BlockVector(0, getCenter().getBlockY(), 0), spinx, spiny, spinz).getBlockY(); + int ty = config.getTranslate().translate(new BlockVector(0, getCenter().getBlockY(), 0), config.getRotation(), spinx, spiny, spinz).getBlockY(); + int y = -1; + int xx, zz; + int yrand = config.getTranslate().getYRandom(); + yrand = yrand > 0 ? rng.i(0, yrand) : yrand < 0 ? rng.i(yrand, 0) : yrand; + boolean bail = false; + + if (yv < 0) { + if (config.getMode().equals(ObjectPlaceMode.CENTER_HEIGHT) || config.getMode() == ObjectPlaceMode.CENTER_STILT) { + y = (c != null ? c.getSurface() : placer.getHighest(x, z, getLoader(), config.isUnderwater())) + rty; + if (!config.isForcePlace()) { + if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) { + bail = true; + } + } + } else if (config.getMode().equals(ObjectPlaceMode.MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.STILT)) { + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + for (int i = minX; i <= maxX; i++) { + for (int ii = minZ; ii <= maxZ; ii++) { + int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; + if (!config.isForcePlace()) { + if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { + bail = true; + break; + } + } + if (h > y) + y = h; + } + } + } else if (config.getMode().equals(ObjectPlaceMode.FAST_MAX_HEIGHT) || config.getMode().equals(ObjectPlaceMode.FAST_STILT)) { + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + + int xRadius = (rotatedDimensions.getBlockX() / 2); + int xLength = xRadius + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zRadius = (rotatedDimensions.getBlockZ() / 2); + int zLength = zRadius + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + + for (int i = minX; i <= maxX; i += Math.abs(xRadius) + 1) { + for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) { + int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; + if (!config.isForcePlace()) { + if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { + bail = true; + break; + } + } + if (h > y) + y = h; + } + } + } else if (config.getMode().equals(ObjectPlaceMode.MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.MIN_STILT) { + y = rdata.getEngine().getHeight() + 1; + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + + int xLength = (rotatedDimensions.getBlockX() / 2) + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zLength = (rotatedDimensions.getBlockZ() / 2) + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + for (int i = minX; i <= maxX; i++) { + for (int ii = minZ; ii <= maxZ; ii++) { + int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; + if (!config.isForcePlace()) { + if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { + bail = true; + break; + } + } + if (h < y) { + y = h; + } + } + } + } else if (config.getMode().equals(ObjectPlaceMode.FAST_MIN_HEIGHT) || config.getMode() == ObjectPlaceMode.FAST_MIN_STILT) { + y = rdata.getEngine().getHeight() + 1; + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + BlockVector rotatedDimensions = config.getRotation().rotate(new BlockVector(getW(), getH(), getD()), spinx, spiny, spinz).clone(); + + int xRadius = (rotatedDimensions.getBlockX() / 2); + int xLength = xRadius + offset.getBlockX(); + int minX = Math.min(x - xLength, x + xLength); + int maxX = Math.max(x - xLength, x + xLength); + int zRadius = (rotatedDimensions.getBlockZ() / 2); + int zLength = zRadius + offset.getBlockZ(); + int minZ = Math.min(z - zLength, z + zLength); + int maxZ = Math.max(z - zLength, z + zLength); + + for (int i = minX; i <= maxX; i += Math.abs(xRadius) + 1) { + for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) { + int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty; + if (!config.isForcePlace()) { + if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) { + bail = true; + break; + } + } + if (h < y) { + y = h; + } + } + } + } else if (config.getMode().equals(ObjectPlaceMode.PAINT)) { + y = placer.getHighest(x, z, getLoader(), config.isUnderwater()) + rty; + if (!config.isForcePlace()) { + if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) { + bail = true; + } + } + } + } else { + y = yv; + if (!config.isForcePlace()) { + if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) { + bail = true; + } + } + } + + if (yv >= 0 && config.isBottom()) { + y += Math.floorDiv(h, 2); + if (!config.isForcePlace()) { + bail = placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z); + } + } + + if (bail && !config.isForcePlace()) { + return -1; + } + + if (yv < 0) { + if (!config.isForcePlace() && !config.isUnderwater() && !config.isOnwater() && placer.isUnderwater(x, z)) { + return -1; + } + } + + if (!config.isForcePlace() && c != null && Math.max(0, h + yrand + ty) + 1 >= c.getHeight()) { + return -1; + } + + if (!config.isForcePlace() && config.isUnderwater() && y + rty + ty >= placer.getFluidHeight()) { + return -1; + } + + if (!config.isForcePlace() && !config.getClamp().canPlace(y + rty + ty, y - rty + ty)) { + return -1; + } + + if (!config.isForcePlace() && (!config.getAllowedCollisions().isEmpty() || !config.getForbiddenCollisions().isEmpty())) { + Engine engine = rdata.getEngine(); + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + for (int i = x - Math.floorDiv(w, 2) + (int) offset.getX(); i <= x + Math.floorDiv(w, 2) - (w % 2 == 0 ? 1 : 0) + (int) offset.getX(); i++) { + for (int j = y - Math.floorDiv(h, 2) + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { + for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { + PlacedObject p = engine.getObjectPlacement(i, j, k); + if (p == null) continue; + IrisObject o = p.getObject(); + if (o == null) continue; + String key = o.getLoadKey(); + if (key != null) { + if (config.getForbiddenCollisions().contains(key) && !config.getAllowedCollisions().contains(key)) { + // Iris.debug("%s collides with %s (%s / %s / %s)", getLoadKey(), key, i, j, k); + return -1; + } + } + } + } + } + } + + if (config.isBore()) { + BlockVector offset = new BlockVector(config.getTranslate().getX(), config.getTranslate().getY(), config.getTranslate().getZ()); + for (int i = x - Math.floorDiv(w, 2) + (int) offset.getX(); i <= x + Math.floorDiv(w, 2) - (w % 2 == 0 ? 1 : 0) + (int) offset.getX(); i++) { + for (int j = y - Math.floorDiv(h, 2) - config.getBoreExtendMinY() + (int) offset.getY(); j <= y + Math.floorDiv(h, 2) + config.getBoreExtendMaxY() - (h % 2 == 0 ? 1 : 0) + (int) offset.getY(); j++) { + for (int k = z - Math.floorDiv(d, 2) + (int) offset.getZ(); k <= z + Math.floorDiv(d, 2) - (d % 2 == 0 ? 1 : 0) + (int) offset.getX(); k++) { + placer.set(i, j, k, AIR); + } + } + } + } + + int lowest = Integer.MAX_VALUE; + y += yrand; + readLock.lock(); + + KMap markers = null; + + try { + if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) { + markers = new KMap<>(); + for (IrisObjectMarker j : config.getMarkers()) { + IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker()); + + if (marker == null) { + continue; + } + + int max = j.getMaximumMarkers(); + + for (BlockVector i : getBlocks().k().shuffle()) { + if (max <= 0) { + break; + } + + BlockData data = getBlocks().get(i); + + for (BlockData k : j.getMark(rdata)) { + if (max <= 0) { + break; + } + + if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { + boolean a = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 1, 0)))); + boolean fff = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 2, 0)))); + + if (!marker.isEmptyAbove() || (a && fff)) { + markers.put(i, j.getMarker()); + max--; + } + } + } + } + } + } + + for (BlockVector g : getBlocks().keySet()) { + BlockData d; + TileData tile = null; + + try { + d = getBlocks().get(g); + tile = getStates().get(g); + } catch (Throwable e) { + Iris.reportError(e); + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)"); + d = AIR; + } + + if (d == null) { + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (null)"); + d = AIR; + } + + BlockVector i = g.clone(); + BlockData data = d.clone(); + i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); + i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); + + if (stilting && i.getBlockY() < lowest && !B.isAir(data)) { + lowest = i.getBlockY(); + } + + if (placer.isPreventingDecay() && (data) instanceof Leaves && !((Leaves) (data)).isPersistent()) { + ((Leaves) data).setPersistent(true); + } + + for (IrisObjectReplace j : config.getEdit()) { + if (rng.chance(j.getChance())) { + for (BlockData k : j.getFind(rdata)) { + if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { + BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); + + if (newData.getMaterial() == data.getMaterial() && !(newData instanceof IrisBlockData || data instanceof IrisBlockData)) + data = data.merge(newData); + else + data = newData; + + if (newData.getMaterial() == Material.SPAWNER) { + Optional> t = j.getReplace().getTile(rng, x, y, z, rdata); + if (t.isPresent()) { + tile = t.get(); + } + } + } + } + } + } + + data = config.getRotation().rotate(data, spinx, spiny, spinz); + xx = x + (int) Math.round(i.getX()); + + int yy = y + (int) Math.round(i.getY()); + zz = z + (int) Math.round(i.getZ()); + + if (warped) { + xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); + zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); + } + + if (yv < 0 && (config.getMode().equals(ObjectPlaceMode.PAINT)) && !B.isVineBlock(data)) { + yy = (int) Math.round(i.getY()) + Math.floorDiv(h, 2) + placer.getHighest(xx, zz, getLoader(), config.isUnderwater()); + } + + if (heightmap != null) { + Position2 pos = new Position2(xx, zz); + + if (!heightmap.containsKey(pos)) { + heightmap.put(pos, yy); + } + + if (heightmap.get(pos) < yy) { + heightmap.put(pos, yy); + } + } + + if (config.isMeld() && !placer.isSolid(xx, yy, zz)) { + continue; + } + + if ((config.isWaterloggable() || config.isUnderwater()) && yy <= placer.getFluidHeight() && data instanceof Waterlogged) { + // TODO Here + ((Waterlogged) data).setWaterlogged(true); + } + + if (B.isVineBlock(data)) { + MultipleFacing f = (MultipleFacing) data; + for (BlockFace face : f.getAllowedFaces()) { + BlockData facingBlock = placer.get(xx + face.getModX(), yy + face.getModY(), zz + face.getModZ()); + if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { + f.setFace(face, true); + } + } + } + + if (listener != null) { + listener.accept(new BlockPosition(xx, yy, zz), data); + } + + if (markers != null && markers.containsKey(g)) { + placer.getEngine().getMantle().getMantle().set(xx, yy, zz, new MatterMarker(markers.get(g))); + } + + boolean wouldReplace = B.isSolid(placer.get(xx, yy, zz)) && B.isVineBlock(data); + + if (!data.getMaterial().equals(Material.AIR) && !data.getMaterial().equals(Material.CAVE_AIR) && !wouldReplace) { + placer.set(xx, yy, zz, data); + if (tile != null) { + placer.setTile(xx, yy, zz, tile); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + Iris.reportError(e); + } + readLock.unlock(); + + if (stilting) { + readLock.lock(); + IrisStiltSettings settings = config.getStiltSettings(); + for (BlockVector g : getBlocks().keySet()) { + BlockData d; + + if (settings == null || settings.getPalette() == null) { + try { + d = getBlocks().get(g); + } catch (Throwable e) { + Iris.reportError(e); + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)"); + d = AIR; + } + + if (d == null) { + Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt null)"); + d = AIR; + } + } else + d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata); + + + BlockVector i = g.clone(); + i = config.getRotation().rotate(i.clone(), spinx, spiny, spinz).clone(); + i = config.getTranslate().translate(i.clone(), config.getRotation(), spinx, spiny, spinz).clone(); + d = config.getRotation().rotate(d, spinx, spiny, spinz); + + if (i.getBlockY() != lowest) + continue; + + for (IrisObjectReplace j : config.getEdit()) { + if (rng.chance(j.getChance())) { + for (BlockData k : j.getFind(rdata)) { + if (j.isExact() ? k.matches(d) : k.getMaterial().equals(d.getMaterial())) { + BlockData newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata).clone(); + + if (newData.getMaterial() == d.getMaterial()) { + d = d.merge(newData); + } else { + d = newData; + } + } + } + } + } + + if (d == null || B.isAir(d)) + continue; + + xx = x + (int) Math.round(i.getX()); + zz = z + (int) Math.round(i.getZ()); + + if (warped) { + xx += config.warp(rng, i.getX() + x, i.getY() + y, i.getZ() + z, getLoader()); + zz += config.warp(rng, i.getZ() + z, i.getY() + y, i.getX() + x, getLoader()); + } + + int highest = placer.getHighest(xx, zz, getLoader(), true); + + if ((config.isWaterloggable() || config.isUnderwater()) && highest <= placer.getFluidHeight() && d instanceof Waterlogged) + ((Waterlogged) d).setWaterlogged(true); + + if (yv >= 0 && config.isBottom()) + y += Math.floorDiv(h, 2); + + int lowerBound = highest - 1; + if (settings != null) { + lowerBound -= config.getStiltSettings().getOverStilt() - rng.i(0, config.getStiltSettings().getYRand()); + if (settings.getYMax() != 0) + lowerBound -= Math.min(config.getStiltSettings().getYMax() - (lowest + y - highest), 0); + } + for (int j = lowest + y; j > lowerBound; j--) { + if (B.isVineBlock(d)) { + MultipleFacing f = (MultipleFacing) d; + for (BlockFace face : f.getAllowedFaces()) { + BlockData facingBlock = placer.get(xx + face.getModX(), j + face.getModY(), zz + face.getModZ()); + if (B.isSolid(facingBlock) && !B.isVineBlock(facingBlock)) { + f.setFace(face, true); + } + } + } + placer.set(xx, j, zz, d); + } + + } + + readLock.unlock(); + } + + if (heightmap != null) { + RNG rngx = rng.nextParallelRNG(3468854); + + for (Position2 i : heightmap.k()) { + int vx = i.getX(); + int vy = heightmap.get(i); + int vz = i.getZ(); + + if (config.getSnow() > 0) { + int height = rngx.i(0, (int) (config.getSnow() * 7)); + placer.set(vx, vy + 1, vz, SNOW_LAYERS[Math.max(Math.min(height, 7), 0)]); + } + } + } + + return y; + } + + public IrisObject rotateCopy(IrisObjectRotation rt) { + IrisObject copy = copy(); + copy.rotate(rt, 0, 0, 0); + return copy; + } + + public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) { + KMap d = new KMap<>(); + + for (BlockVector i : getBlocks().keySet()) { + d.put(r.rotate(i.clone(), spinx, spiny, spinz), r.rotate(getBlocks().get(i).clone(), + spinx, spiny, spinz)); + } + + KMap> dx = new KMap<>(); + + for (BlockVector i : getStates().keySet()) { + dx.put(r.rotate(i.clone(), spinx, spiny, spinz), getStates().get(i)); + } + + blocks = d; + states = dx; + shrinkwrap(); + } + + public void place(Location at) { + for (BlockVector i : getBlocks().keySet()) { + Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock(); + b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); + + if (getStates().containsKey(i)) { + Iris.info(Objects.requireNonNull(states.get(i)).toString()); + BlockState st = b.getState(); + Objects.requireNonNull(getStates().get(i)).toBukkitTry(st); + st.update(); + } + } + } + + public void placeCenterY(Location at) { + for (BlockVector i : getBlocks().keySet()) { + Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock(); + b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); + + if (getStates().containsKey(i)) { + Objects.requireNonNull(getStates().get(i)).toBukkitTry(b.getState()); + } + } + } + + public synchronized KMap getBlocks() { + return blocks; + } + + public synchronized KMap> getStates() { + return states; + } + + public void unplaceCenterY(Location at) { + for (BlockVector i : getBlocks().keySet()) { + at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false); + } + } + + public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) { + Vector sm1 = new Vector(scale - 1, scale - 1, scale - 1); + scale = Math.max(0.001, Math.min(50, scale)); + if (scale < 1) { + scale = scale - 0.0001; + } + + IrisPosition l1 = getAABB().max(); + IrisPosition l2 = getAABB().min(); + @SuppressWarnings({"unchecked", "rawtypes"}) HashMap placeBlock = new HashMap(); + + Vector center = getCenter(); + if (getH() == 2) { + center = center.setY(center.getBlockY() + 0.5); + } + if (getW() == 2) { + center = center.setX(center.getBlockX() + 0.5); + } + if (getD() == 2) { + center = center.setZ(center.getBlockZ() + 0.5); + } + + IrisObject oo = new IrisObject((int) Math.ceil((w * scale) + (scale * 2)), (int) Math.ceil((h * scale) + (scale * 2)), (int) Math.ceil((d * scale) + (scale * 2))); + + for (Map.Entry entry : blocks.entrySet()) { + BlockData bd = entry.getValue(); + placeBlock.put(entry.getKey().clone().add(HALF).subtract(center) + .multiply(scale).add(sm1).toBlockVector(), bd); + } + + for (Map.Entry entry : placeBlock.entrySet()) { + BlockVector v = entry.getKey(); + if (scale > 1) { + for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) { + oo.getBlocks().put(vec, entry.getValue()); + } + } else { + oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue()); + } + } + + if (scale > 1) { + switch (interpolation) { + case TRILINEAR -> oo.trilinear((int) Math.round(scale)); + case TRICUBIC -> oo.tricubic((int) Math.round(scale)); + case TRIHERMITE -> oo.trihermite((int) Math.round(scale)); + } + } + + return oo; + } + + public void trilinear(int rad) { + KMap v = getBlocks().copy(); + KMap b = new KMap<>(); + BlockVector min = getAABB().minbv(); + BlockVector max = getAABB().maxbv(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (IrisInterpolation.getTrilinear(x, y, z, rad, (xx, yy, zz) -> { + BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); + + if (data == null || data.getMaterial().isAir()) { + return 0; + } + + return 1; + }) >= 0.5) { + b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); + } else { + b.put(new BlockVector(x, y, z), AIR); + } + } + } + } + + blocks = b; + } + + public void tricubic(int rad) { + KMap v = getBlocks().copy(); + KMap b = new KMap<>(); + BlockVector min = getAABB().minbv(); + BlockVector max = getAABB().maxbv(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (IrisInterpolation.getTricubic(x, y, z, rad, (xx, yy, zz) -> { + BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); + + if (data == null || data.getMaterial().isAir()) { + return 0; + } + + return 1; + }) >= 0.5) { + b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); + } else { + b.put(new BlockVector(x, y, z), AIR); + } + } + } + } + + blocks = b; + } + + public void trihermite(int rad) { + trihermite(rad, 0D, 0D); + } + + public void trihermite(int rad, double tension, double bias) { + KMap v = getBlocks().copy(); + KMap b = new KMap<>(); + BlockVector min = getAABB().minbv(); + BlockVector max = getAABB().maxbv(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (IrisInterpolation.getTrihermite(x, y, z, rad, (xx, yy, zz) -> { + BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz)); + + if (data == null || data.getMaterial().isAir()) { + return 0; + } + + return 1; + }, tension, bias) >= 0.5) { + b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z)); + } else { + b.put(new BlockVector(x, y, z), AIR); + } + } + } + } + + blocks = b; + } + + private BlockData nearestBlockData(int x, int y, int z) { + BlockVector vv = new BlockVector(x, y, z); + BlockData r = getBlocks().get(vv); + + if (r != null && !r.getMaterial().isAir()) { + return r; + } + + double d = Double.MAX_VALUE; + + for (Map.Entry entry : blocks.entrySet()) { + BlockData dat = entry.getValue(); + + if (dat.getMaterial().isAir()) { + continue; + } + + double dx = entry.getKey().distanceSquared(vv); + + if (dx < d) { + d = dx; + r = dat; + } + } + + return r; + } + + public int volume() { + return blocks.size(); + } + + @Override + public String getFolderName() { + return "objects"; + } + + @Override + public String getTypeName() { + return "Object"; + } + + @Override + public void scanForErrors(JSONObject p, VolmitSender sender) { + } +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java index 813ef8c4a..d03248e87 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectPlacement.java @@ -136,6 +136,8 @@ public class IrisObjectPlacement { @ArrayType(type = String.class) @Desc("List of objects to this object is forbidden to collied with") private KList forbiddenCollisions = new KList<>(); + @Desc("Ignore any placement restrictions for this object") + private boolean forcePlace = false; private transient AtomicCache cache = new AtomicCache<>(); public IrisObjectPlacement toPlacement(String... place) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java index 5b75ef725..6e8eb2a11 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObjectRotation.java @@ -101,6 +101,11 @@ public class IrisObjectRotation { i.setPosition(rotate(i.getPosition())); i.setDirection(rotate(i.getDirection())); } + try { + var translate = piece.getPlacementOptions().getTranslate(); + var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ())); + translate.setX(pos.getX()).setY(pos.getY()).setZ(pos.getZ()); + } catch (NullPointerException ignored) {} return piece; } 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 f63b6d12a..ce0bebd74 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 @@ -1,413 +1,444 @@ -/* - * 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.platform; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.service.StudioSVC; -import com.volmit.iris.engine.IrisEngine; -import com.volmit.iris.engine.data.chunk.TerrainChunk; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.framework.EngineTarget; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.engine.object.IrisWorld; -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; -import com.volmit.iris.util.scheduling.ChronoLatch; -import com.volmit.iris.util.scheduling.J; -import com.volmit.iris.util.scheduling.Looper; -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; -import org.bukkit.event.Listener; -import org.bukkit.event.world.WorldInitEvent; -import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.generator.WorldInfo; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.lang.reflect.Field; -import java.util.List; -import java.util.Random; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Consumer; - -@EqualsAndHashCode(callSuper = true) -@Data -public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator, Listener { - private static final int LOAD_LOCKS = Runtime.getRuntime().availableProcessors() * 4; - private final Semaphore loadLock; - private final IrisWorld world; - private final File dataLocation; - private final String dimensionKey; - private final ReactiveFolder folder; - private final ReentrantLock lock = new ReentrantLock(); - private final KList populators; - private final ChronoLatch hotloadChecker; - private final AtomicBoolean setup; - private final boolean studio; - private final AtomicInteger a = new AtomicInteger(0); - private Engine engine; - private Looper hotloader; - private StudioMode lastMode; - private DummyBiomeProvider dummyBiomeProvider; - @Setter - private StudioGenerator studioGenerator; - - private boolean initialized = false; - - public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) { - setup = new AtomicBoolean(false); - studioGenerator = null; - dummyBiomeProvider = new DummyBiomeProvider(); - populators = new KList<>(); - loadLock = new Semaphore(LOAD_LOCKS); - this.world = world; - this.hotloadChecker = new ChronoLatch(1000, false); - this.studio = studio; - this.dataLocation = dataLocation; - this.dimensionKey = dimensionKey; - this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> hotload()); - Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); - } - - private static Field getField(Class clazz, String fieldName) - throws NoSuchFieldException { - try { - return clazz.getDeclaredField(fieldName); - } catch (NoSuchFieldException e) { - Class superClass = clazz.getSuperclass(); - if (superClass == null) { - throw e; - } else { - return getField(superClass, fieldName); - } - } - } - - @EventHandler - public void onWorldInit(WorldInitEvent event) { - try { - if (!initialized) { - world.setRawWorldSeed(event.getWorld().getSeed()); - if (world.name().equals(event.getWorld().getName())) { - INMS.get().inject(event.getWorld().getSeed(), getEngine(event.getWorld()), event.getWorld()); - Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); - initialized = true; - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - - private void setupEngine() { - IrisData data = IrisData.get(dataLocation); - IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); - - if (dimension == null) { - Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); - IrisDimension test = IrisData.loadAnyDimension(dimensionKey); - - if (test != null) { - Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " "); - Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); - Iris.warn("Attempted to install into " + data.getDataFolder().getPath()); - data.dump(); - data.clearLists(); - test = data.getDimensionLoader().load(dimensionKey); - - if (test != null) { - Iris.success("Woo! Patched the Engine!"); - dimension = test; - } else { - Iris.error("Failed to patch dimension!"); - throw new RuntimeException("Missing Dimension: " + dimensionKey); - } - } else { - Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?"); - throw new RuntimeException("Missing Dimension: " + dimensionKey); - } - } - - lastMode = StudioMode.NORMAL; - engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); - populators.clear(); - } - - @Override - public void injectChunkReplacement(World world, int x, int z, Consumer jobs) { - try { - loadLock.acquire(); - IrisBiomeStorage st = new IrisBiomeStorage(); - TerrainChunk tc = TerrainChunk.createUnsafe(world, st); - Hunk blocks = Hunk.view(tc); - Hunk biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()); - this.world.bind(world); - getEngine().generate(x << 4, z << 4, blocks, biomes, true); - Iris.debug("Regenerated " + x + " " + z); - int t = 0; - for (int i = getEngine().getHeight() >> 4; i >= 0; i--) { - if (!world.isChunkLoaded(x, z)) { - continue; - } - - Chunk c = world.getChunkAt(x, z); - for (Entity ee : c.getEntities()) { - if (ee instanceof Player) { - continue; - } - - J.s(ee::remove); - } - - J.s(() -> engine.getWorldManager().onChunkLoad(c, false)); - - int finalI = i; - jobs.accept(() -> { - - for (int xx = 0; xx < 16; xx++) { - for (int yy = 0; yy < 16; yy++) { - for (int zz = 0; zz < 16; zz++) { - if (yy + (finalI << 4) >= engine.getHeight() || yy + (finalI << 4) < 0) { - continue; - } - c.getBlock(xx, yy + (finalI << 4) + world.getMinHeight(), zz) - .setBlockData(tc.getBlockData(xx, yy + (finalI << 4) + world.getMinHeight(), zz), false); - } - } - } - }); - } - - loadLock.release(); - } catch (Throwable e) { - loadLock.release(); - Iris.error("======================================"); - e.printStackTrace(); - Iris.reportErrorChunk(x, z, e, "CHUNK"); - Iris.error("======================================"); - - ChunkData d = Bukkit.createChunkData(world); - - for (int i = 0; i < 16; i++) { - for (int j = 0; j < 16; j++) { - d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData()); - } - } - } - } - - private Engine getEngine(WorldInfo world) { - if (setup.get()) { - return getEngine(); - } - - lock.lock(); - - if (setup.get()) { - return getEngine(); - } - - - setup.set(true); - getWorld().setRawWorldSeed(world.getSeed()); - setupEngine(); - this.hotloader = studio ? new Looper() { - @Override - protected long loop() { - if (hotloadChecker.flip()) { - folder.check(); - } - - return 250; - } - } : null; - - if (studio) { - hotloader.setPriority(Thread.MIN_PRIORITY); - hotloader.start(); - hotloader.setName(getTarget().getWorld().name() + " Hotloader"); - } - - lock.unlock(); - - return engine; - } - - @Override - public void close() { - withExclusiveControl(() -> { - if (isStudio()) { - hotloader.interrupt(); - } - - getEngine().close(); - folder.clear(); - populators.clear(); - - }); - } - - @Override - public boolean isStudio() { - return studio; - } - - @Override - public void hotload() { - if (!isStudio()) { - return; - } - - withExclusiveControl(() -> getEngine().hotload()); - } - - public void withExclusiveControl(Runnable r) { - J.a(() -> { - try { - loadLock.acquire(LOAD_LOCKS); - r.run(); - loadLock.release(LOAD_LOCKS); - } catch (Throwable e) { - Iris.reportError(e); - } - }); - } - - @Override - public void touch(World world) { - getEngine(world); - } - - @Override - public void generateNoise(@NotNull WorldInfo world, @NotNull Random random, int x, int z, @NotNull ChunkGenerator.ChunkData d) { - try { - getEngine(world); - computeStudioGenerator(); - TerrainChunk tc = TerrainChunk.create(d, new IrisBiomeStorage()); - this.world.bind(world); - if (studioGenerator != null) { - studioGenerator.generateChunk(getEngine(), tc, x, z); - } else { - ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); - BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight()); - getEngine().generate(x << 4, z << 4, blocks, biomes, false); - blocks.apply(); - biomes.apply(); - } - - Iris.debug("Generated " + x + " " + z); - } catch (Throwable e) { - Iris.error("======================================"); - e.printStackTrace(); - Iris.reportErrorChunk(x, z, e, "CHUNK"); - Iris.error("======================================"); - - for (int i = 0; i < 16; i++) { - for (int j = 0; j < 16; j++) { - d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData()); - } - } - } - } - - @Override - public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) { - return 4; - } - - private void computeStudioGenerator() { - if (!getEngine().getDimension().getStudioMode().equals(lastMode)) { - lastMode = getEngine().getDimension().getStudioMode(); - getEngine().getDimension().getStudioMode().inject(this); - } - } - - @NotNull - @Override - public List getDefaultPopulators(@NotNull World world) { - return populators; - } - - @Override - public boolean isParallelCapable() { - return true; - } - - @Override - public boolean shouldGenerateCaves() { - return false; - } - - @Override - public boolean shouldGenerateDecorations() { - return false; - } - - @Override - public boolean shouldGenerateMobs() { - return false; - } - - @Override - public boolean shouldGenerateStructures() { - return false; - } - - @Override - public boolean shouldGenerateNoise() { - return false; - } - - @Override - public boolean shouldGenerateSurface() { - return false; - } - - @Override - public boolean shouldGenerateBedrock() { - return false; - } - - @Nullable - @Override - public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) { - return dummyBiomeProvider; - } -} +/* + * 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.platform; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.service.StudioSVC; +import com.volmit.iris.engine.IrisEngine; +import com.volmit.iris.engine.data.chunk.TerrainChunk; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.EngineTarget; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.engine.object.IrisWorld; +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; +import com.volmit.iris.util.scheduling.ChronoLatch; +import com.volmit.iris.util.scheduling.J; +import com.volmit.iris.util.scheduling.Looper; +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; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Consumer; + +@EqualsAndHashCode(callSuper = true) +@Data +public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator, Listener { + private static final int LOAD_LOCKS = Runtime.getRuntime().availableProcessors() * 4; + private final Semaphore loadLock; + private final IrisWorld world; + private final File dataLocation; + private final String dimensionKey; + private final ReactiveFolder folder; + private final ReentrantLock lock = new ReentrantLock(); + private final KList populators; + private final ChronoLatch hotloadChecker; + private final AtomicBoolean setup; + private final boolean studio; + private final AtomicInteger a = new AtomicInteger(0); + private final boolean smartVanillaHeight; + private Engine engine; + private Looper hotloader; + private StudioMode lastMode; + private DummyBiomeProvider dummyBiomeProvider; + @Setter + private StudioGenerator studioGenerator; + + private boolean initialized = false; + + public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey, boolean smartVanillaHeight) { + setup = new AtomicBoolean(false); + studioGenerator = null; + dummyBiomeProvider = new DummyBiomeProvider(); + populators = new KList<>(); + loadLock = new Semaphore(LOAD_LOCKS); + this.world = world; + this.hotloadChecker = new ChronoLatch(1000, false); + this.studio = studio; + this.dataLocation = dataLocation; + this.dimensionKey = dimensionKey; + this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> hotload()); + this.smartVanillaHeight = smartVanillaHeight; + Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); + } + + private static Field getField(Class clazz, String fieldName) + throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw e; + } else { + return getField(superClass, fieldName); + } + } + } + + @EventHandler + public void onWorldInit(WorldInitEvent event) { + try { + if (!initialized) { + world.setRawWorldSeed(event.getWorld().getSeed()); + if (world.name().equals(event.getWorld().getName())) { + Engine engine = getEngine(event.getWorld()); + if (engine == null) { + Iris.warn("Failed to get Engine!"); + J.s(() -> { + Engine engine1 = getEngine(event.getWorld()); + if (engine1 != null) { + try { + INMS.get().inject(event.getWorld().getSeed(), engine1, event.getWorld()); + Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); + initialized = true; + } catch (Throwable e) { + e.printStackTrace(); + } + } + }, 10); + } else { + INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld()); + Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); + initialized = true; + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private void setupEngine() { + IrisData data = IrisData.get(dataLocation); + IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); + + if (dimension == null) { + Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); + IrisDimension test = IrisData.loadAnyDimension(dimensionKey); + + if (test != null) { + Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " "); + Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); + Iris.warn("Attempted to install into " + data.getDataFolder().getPath()); + data.dump(); + data.clearLists(); + test = data.getDimensionLoader().load(dimensionKey); + + if (test != null) { + Iris.success("Woo! Patched the Engine!"); + dimension = test; + } else { + Iris.error("Failed to patch dimension!"); + throw new RuntimeException("Missing Dimension: " + dimensionKey); + } + } else { + Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?"); + throw new RuntimeException("Missing Dimension: " + dimensionKey); + } + } + if (smartVanillaHeight) { + dimension.setSmartVanillaHeight(true); + try (FileWriter writer = new FileWriter(data.getDimensionLoader().fileFor(dimension))) { + writer.write(data.getGson().toJson(dimension)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + lastMode = StudioMode.NORMAL; + engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); + populators.clear(); + } + + @Override + public void injectChunkReplacement(World world, int x, int z, Consumer jobs) { + try { + loadLock.acquire(); + IrisBiomeStorage st = new IrisBiomeStorage(); + TerrainChunk tc = TerrainChunk.createUnsafe(world, st); + Hunk blocks = Hunk.view(tc); + Hunk biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()); + this.world.bind(world); + getEngine().generate(x << 4, z << 4, blocks, biomes, true); + Iris.debug("Regenerated " + x + " " + z); + int t = 0; + for (int i = getEngine().getHeight() >> 4; i >= 0; i--) { + if (!world.isChunkLoaded(x, z)) { + continue; + } + + Chunk c = world.getChunkAt(x, z); + for (Entity ee : c.getEntities()) { + if (ee instanceof Player) { + continue; + } + + J.s(ee::remove); + } + + J.s(() -> engine.getWorldManager().onChunkLoad(c, false)); + + int finalI = i; + jobs.accept(() -> { + + for (int xx = 0; xx < 16; xx++) { + for (int yy = 0; yy < 16; yy++) { + for (int zz = 0; zz < 16; zz++) { + if (yy + (finalI << 4) >= engine.getHeight() || yy + (finalI << 4) < 0) { + continue; + } + c.getBlock(xx, yy + (finalI << 4) + world.getMinHeight(), zz) + .setBlockData(tc.getBlockData(xx, yy + (finalI << 4) + world.getMinHeight(), zz), false); + } + } + } + }); + } + + loadLock.release(); + } catch (Throwable e) { + loadLock.release(); + Iris.error("======================================"); + e.printStackTrace(); + Iris.reportErrorChunk(x, z, e, "CHUNK"); + Iris.error("======================================"); + + ChunkData d = Bukkit.createChunkData(world); + + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData()); + } + } + } + } + + private Engine getEngine(WorldInfo world) { + if (setup.get()) { + return getEngine(); + } + + lock.lock(); + + try { + if (setup.get()) { + return getEngine(); + } + + + getWorld().setRawWorldSeed(world.getSeed()); + setupEngine(); + setup.set(true); + this.hotloader = studio ? new Looper() { + @Override + protected long loop() { + if (hotloadChecker.flip()) { + folder.check(); + } + + return 250; + } + } : null; + + if (studio) { + hotloader.setPriority(Thread.MIN_PRIORITY); + hotloader.start(); + hotloader.setName(getTarget().getWorld().name() + " Hotloader"); + } + + return engine; + } finally { + lock.unlock(); + } + } + + @Override + public void close() { + withExclusiveControl(() -> { + if (isStudio()) { + hotloader.interrupt(); + } + + getEngine().close(); + folder.clear(); + populators.clear(); + + }); + } + + @Override + public boolean isStudio() { + return studio; + } + + @Override + public void hotload() { + if (!isStudio()) { + return; + } + + withExclusiveControl(() -> getEngine().hotload()); + } + + public void withExclusiveControl(Runnable r) { + J.a(() -> { + try { + loadLock.acquire(LOAD_LOCKS); + r.run(); + loadLock.release(LOAD_LOCKS); + } catch (Throwable e) { + Iris.reportError(e); + } + }); + } + + @Override + public void touch(World world) { + getEngine(world); + } + + @Override + public void generateNoise(@NotNull WorldInfo world, @NotNull Random random, int x, int z, @NotNull ChunkGenerator.ChunkData d) { + try { + getEngine(world); + computeStudioGenerator(); + TerrainChunk tc = TerrainChunk.create(d, new IrisBiomeStorage()); + this.world.bind(world); + if (studioGenerator != null) { + studioGenerator.generateChunk(getEngine(), tc, x, z); + } else { + ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); + BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight()); + getEngine().generate(x << 4, z << 4, blocks, biomes, false); + blocks.apply(); + biomes.apply(); + } + + Iris.debug("Generated " + x + " " + z); + } catch (Throwable e) { + Iris.error("======================================"); + e.printStackTrace(); + Iris.reportErrorChunk(x, z, e, "CHUNK"); + Iris.error("======================================"); + + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData()); + } + } + } + } + + @Override + public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) { + return 4; + } + + private void computeStudioGenerator() { + if (!getEngine().getDimension().getStudioMode().equals(lastMode)) { + lastMode = getEngine().getDimension().getStudioMode(); + getEngine().getDimension().getStudioMode().inject(this); + } + } + + @NotNull + @Override + public List getDefaultPopulators(@NotNull World world) { + return populators; + } + + @Override + public boolean isParallelCapable() { + return true; + } + + @Override + public boolean shouldGenerateCaves() { + return false; + } + + @Override + public boolean shouldGenerateDecorations() { + return false; + } + + @Override + public boolean shouldGenerateMobs() { + return false; + } + + @Override + public boolean shouldGenerateStructures() { + return false; + } + + @Override + public boolean shouldGenerateNoise() { + return false; + } + + @Override + public boolean shouldGenerateSurface() { + return false; + } + + @Override + public boolean shouldGenerateBedrock() { + return false; + } + + @Nullable + @Override + public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) { + return dummyBiomeProvider; + } +} 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 59d150357..d67615151 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 @@ -25,6 +25,7 @@ import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.object.IrisCompat; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.misc.E; import com.volmit.iris.util.scheduling.ChronoLatch; import it.unimi.dsi.fastutil.ints.*; import org.bukkit.Bukkit; @@ -46,6 +47,7 @@ public class B { private static final KMap custom = new KMap<>(); private static final Material AIR_MATERIAL = Material.AIR; + private static final Material SHORT_GRASS = E.getOrDefault(Material.class, "GRASS", "SHORT_GRASS"); private static final BlockData AIR = AIR_MATERIAL.createBlockData(); private static final IntSet foliageCache = buildFoliageCache(); private static final IntSet deepslateCache = buildDeepslateCache(); @@ -85,7 +87,7 @@ public class B { WHITE_TULIP, FERN, LARGE_FERN, - GRASS, + SHORT_GRASS, TALL_GRASS }).forEach((i) -> b.add(i.ordinal())); @@ -143,7 +145,7 @@ public class B { private static IntSet buildDecorantCache() { IntSet b = new IntOpenHashSet(); Arrays.stream(new Material[]{ - GRASS, + SHORT_GRASS, TALL_GRASS, TALL_SEAGRASS, FERN, diff --git a/core/src/main/java/com/volmit/iris/util/data/IrisBlockData.java b/core/src/main/java/com/volmit/iris/util/data/IrisBlockData.java new file mode 100644 index 000000000..b5cd4030c --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/data/IrisBlockData.java @@ -0,0 +1,127 @@ +package com.volmit.iris.util.data; + +import com.volmit.iris.core.link.Identifier; +import lombok.Data; +import lombok.NonNull; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.SoundGroup; +import org.bukkit.block.*; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.structure.Mirror; +import org.bukkit.block.structure.StructureRotation; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@Data +public class IrisBlockData implements BlockData { + private final @NonNull BlockData base; + private final @NotNull Identifier custom; + + @NotNull + @Override + public Material getMaterial() { + return base.getMaterial(); + } + + @NotNull + @Override + public String getAsString() { + return base.getAsString(); + } + + @NotNull + @Override + public String getAsString(boolean b) { + return base.getAsString(b); + } + + @NotNull + @Override + public BlockData merge(@NotNull BlockData blockData) { + return new IrisBlockData(base.merge(blockData), custom); + } + + @Override + public boolean matches(@Nullable BlockData blockData) { + if (blockData instanceof IrisBlockData b) + return custom.equals(b.custom) && base.matches(b.base); + return base.matches(blockData); + } + + @NotNull + @Override + public BlockData clone() { + return new IrisBlockData(base.clone(), custom); + } + + @NotNull + @Override + public SoundGroup getSoundGroup() { + return base.getSoundGroup(); + } + + @Override + public int getLightEmission() { + return base.getLightEmission(); + } + + @Override + public boolean isOccluding() { + return base.isOccluding(); + } + + @Override + public boolean requiresCorrectToolForDrops() { + return base.requiresCorrectToolForDrops(); + } + + @Override + public boolean isPreferredTool(@NotNull ItemStack itemStack) { + return base.isPreferredTool(itemStack); + } + + @NotNull + @Override + public PistonMoveReaction getPistonMoveReaction() { + return base.getPistonMoveReaction(); + } + + @Override + public boolean isSupported(@NotNull Block block) { + return base.isSupported(block); + } + + @Override + public boolean isSupported(@NotNull Location location) { + return base.isSupported(location); + } + + @Override + public boolean isFaceSturdy(@NotNull BlockFace blockFace, @NotNull BlockSupport blockSupport) { + return base.isFaceSturdy(blockFace, blockSupport); + } + + @NotNull + @Override + public Material getPlacementMaterial() { + return base.getPlacementMaterial(); + } + + @Override + public void rotate(@NotNull StructureRotation structureRotation) { + base.rotate(structureRotation); + } + + @Override + public void mirror(@NotNull Mirror mirror) { + base.mirror(mirror); + } + + @NotNull + @Override + public BlockState createBlockState() { + return base.createBlockState(); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java b/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java new file mode 100644 index 000000000..489590496 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java @@ -0,0 +1,36 @@ +package com.volmit.iris.util.decree.handlers; + +import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.decree.DecreeParameterHandler; +import com.volmit.iris.util.decree.exceptions.DecreeParsingException; + +public class DataVersionHandler implements DecreeParameterHandler { + @Override + public KList getPossibilities() { + return new KList<>(DataVersion.values()); + } + + @Override + public String toString(DataVersion version) { + return version.getVersion(); + } + + @Override + public DataVersion parse(String in, boolean force) throws DecreeParsingException { + if (in.equalsIgnoreCase("latest")) { + return DataVersion.getLatest(); + } + for (DataVersion v : DataVersion.values()) { + if (v.getVersion().equalsIgnoreCase(in)) { + return v; + } + } + throw new DecreeParsingException("Unable to parse data version \"" + in + "\""); + } + + @Override + public boolean supports(Class type) { + return DataVersion.class.equals(type); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkView.java b/core/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkView.java index a9267f3fc..62cb7fc61 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkView.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/view/ChunkDataHunkView.java @@ -19,6 +19,7 @@ package com.volmit.iris.util.hunk.view; import com.volmit.iris.util.data.B; +import com.volmit.iris.util.data.IrisBlockData; import com.volmit.iris.util.hunk.Hunk; import org.bukkit.block.data.BlockData; import org.bukkit.generator.ChunkGenerator.ChunkData; @@ -72,7 +73,8 @@ public class ChunkDataHunkView implements Hunk { } try { - + if (t instanceof IrisBlockData d) + t = d.getBase(); chunk.setBlock(x, y + chunk.getMinHeight(), z, t); } catch (Throwable ignored) { diff --git a/core/src/main/java/com/volmit/iris/util/inventorygui/UIElement.java b/core/src/main/java/com/volmit/iris/util/inventorygui/UIElement.java index 21344d06f..acadadd37 100644 --- a/core/src/main/java/com/volmit/iris/util/inventorygui/UIElement.java +++ b/core/src/main/java/com/volmit/iris/util/inventorygui/UIElement.java @@ -212,7 +212,7 @@ public class UIElement implements Element { im.setLore(getLore().copy()); if (isEnchanted()) { - im.addEnchant(Enchantment.DURABILITY, 1, true); + im.addEnchant(Enchantment.FIRE_ASPECT, 1, true); } is.setItemMeta(im); 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 e71652a28..b89804537 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 @@ -21,6 +21,7 @@ package com.volmit.iris.util.mantle; import com.google.common.util.concurrent.AtomicDouble; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; @@ -59,6 +60,7 @@ import java.util.concurrent.locks.ReentrantLock; */ public class Mantle { + private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true"); private final File dataFolder; @Getter private final int worldHeight; @@ -423,17 +425,17 @@ public class Mantle { ioTrim.set(true); unloadLock.lock(); try { - if (lastUse != null) { + if (lastUse != null && IrisEngineSVC.instance != null) { if (!lastUse.isEmpty()) { Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0)); - for (Long i : new ArrayList<>(lastUse.keySet())) { + for (long i : new ArrayList<>(lastUse.keySet())) { double finalAdjustedIdleDuration = adjustedIdleDuration.get(); hyperLock.withLong(i, () -> { Long lastUseTime = lastUse.get(i); if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) { toUnload.add(i); Iris.debug("Tectonic Region added to unload"); - //Iris.panic(); + IrisEngineSVC.instance.trimActiveAlive.reset(); } }); } @@ -452,37 +454,47 @@ public class Mantle { AtomicInteger i = new AtomicInteger(); unloadLock.lock(); BurstExecutor burst = null; - try { - KList copy = toUnload.copy(); - burst = MultiBurst.burst.burst(copy.size()); - burst.setMulticore(copy.size() > tectonicLimit); - for (long id : copy) { - burst.queue(() -> - hyperLock.withLong(id, () -> { - TectonicPlate m = loadedRegions.get(id); - if (m != null) { - try { - m.write(fileForRegion(dataFolder, id)); - loadedRegions.remove(id); - 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) { - Iris.reportError(e); - } - } - })); + if (IrisEngineSVC.instance != null) { + try { + KList copy = toUnload.copy(); + if (!disableClear) toUnload.clear(); + burst = MultiBurst.burst.burst(copy.size()); + burst.setMulticore(copy.size() > tectonicLimit); + for (int j = 0; j < copy.size(); j++) { + Long id = copy.get(j); + if (id == null) { + Iris.error("Null id in unloadTectonicPlate at index " + j); + continue; + } - } - burst.complete(); - } catch (Throwable e) { - e.printStackTrace(); - if (burst != null) + burst.queue(() -> + hyperLock.withLong(id, () -> { + TectonicPlate m = loadedRegions.get(id); + if (m != null) { + try { + m.write(fileForRegion(dataFolder, id)); + loadedRegions.remove(id); + lastUse.remove(id); + if (disableClear) toUnload.remove(id); + i.incrementAndGet(); + Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); + IrisEngineSVC.instance.unloadActiveAlive.reset(); + } catch (IOException e) { + Iris.reportError(e); + } + } + })); + } burst.complete(); - } finally { - unloadLock.unlock(); - ioTectonicUnload.set(true); + } catch (Throwable e) { + e.printStackTrace(); + if (burst != null) + burst.complete(); + } finally { + unloadLock.unlock(); + ioTectonicUnload.set(true); + } + return i.get(); } return i.get(); } 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 index 03d98dee6..6bb5f1e6f 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java @@ -33,7 +33,8 @@ public enum MantleFlag { CLEANED, PLANNED, ETCHED, - TILE; + TILE, + CUSTOM; static StateList getStateList() { return new StateList(MantleFlag.values()); diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java b/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java index 9a0de90ba..cf6eef776 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java @@ -18,6 +18,7 @@ package com.volmit.iris.util.matter.slices; +import com.volmit.iris.util.data.IrisBlockData; import com.volmit.iris.util.data.palette.Palette; import com.volmit.iris.util.matter.Sliced; import org.bukkit.Bukkit; @@ -39,7 +40,11 @@ public class BlockMatter extends RawMatter { public BlockMatter(int width, int height, int depth) { super(width, height, depth, BlockData.class); - registerWriter(World.class, ((w, d, x, y, z) -> w.getBlockAt(x, y, z).setBlockData(d))); + registerWriter(World.class, ((w, d, x, y, z) -> { + if (d instanceof IrisBlockData c) + w.getBlockAt(x, y, z).setBlockData(c.getBase()); + else w.getBlockAt(x, y, z).setBlockData(d); + })); registerReader(World.class, (w, x, y, z) -> { BlockData d = w.getBlockAt(x, y, z).getBlockData(); return d.getMaterial().isAir() ? null : d; diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/IdentifierMatter.java b/core/src/main/java/com/volmit/iris/util/matter/slices/IdentifierMatter.java new file mode 100644 index 000000000..5777c6320 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/IdentifierMatter.java @@ -0,0 +1,36 @@ +package com.volmit.iris.util.matter.slices; + +import com.volmit.iris.core.link.Identifier; +import com.volmit.iris.util.data.palette.Palette; +import com.volmit.iris.util.matter.Sliced; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +@Sliced +public class IdentifierMatter extends RawMatter { + + public IdentifierMatter() { + this(1, 1, 1); + } + + public IdentifierMatter(int width, int height, int depth) { + super(width, height, depth, Identifier.class); + } + + @Override + public Palette getGlobalPalette() { + return null; + } + + @Override + public void writeNode(Identifier b, DataOutputStream dos) throws IOException { + dos.writeUTF(b.toString()); + } + + @Override + public Identifier readNode(DataInputStream din) throws IOException { + return Identifier.fromString(din.readUTF()); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java b/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java new file mode 100644 index 000000000..55803b946 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructuresMatter.java @@ -0,0 +1,35 @@ +package com.volmit.iris.util.matter.slices; + +import com.volmit.iris.util.data.palette.Palette; +import com.volmit.iris.util.matter.Sliced; +import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +@Sliced +public class JigsawStructuresMatter extends RawMatter { + public JigsawStructuresMatter() { + this(1, 1, 1); + } + + public JigsawStructuresMatter(int width, int height, int depth) { + super(width, height, depth, JigsawStructuresContainer.class); + } + + @Override + public Palette getGlobalPalette() { + return null; + } + + @Override + public void writeNode(JigsawStructuresContainer b, DataOutputStream dos) throws IOException { + b.write(dos); + } + + @Override + public JigsawStructuresContainer readNode(DataInputStream din) throws IOException { + return new JigsawStructuresContainer(din); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java b/core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java new file mode 100644 index 000000000..68d0f388f --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/container/JigsawStructuresContainer.java @@ -0,0 +1,62 @@ +package com.volmit.iris.util.matter.slices.container; + +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.math.Position2; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class JigsawStructuresContainer { + private final Map> map = new KMap<>(); + + public JigsawStructuresContainer() {} + + public JigsawStructuresContainer(DataInputStream din) throws IOException { + int s0 = din.readInt(); + for (int i = 0; i < s0; i++) { + int s1 = din.readInt(); + KList list = new KList<>(s1); + for (int j = 0; j < s1; j++) { + list.add(new Position2(din.readInt(), din.readInt())); + } + map.put(din.readUTF(), list); + } + } + + public void write(DataOutputStream dos) throws IOException { + dos.writeInt(map.size()); + for (String key : map.keySet()) { + List list = map.get(key); + dos.writeInt(list.size()); + for (Position2 pos : list) { + dos.writeInt(pos.getX()); + dos.writeInt(pos.getZ()); + } + dos.writeUTF(key); + } + } + + @Unmodifiable + public Set getStructures() { + return Collections.unmodifiableSet(map.keySet()); + } + + @Unmodifiable + public List getPositions(String structure) { + return Collections.unmodifiableList(map.get(structure)); + } + + @ChunkCoordinates + public void add(IrisJigsawStructure structure, Position2 pos) { + map.computeIfAbsent(structure.getLoadKey(), k -> new KList<>()).add(pos); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/misc/E.java b/core/src/main/java/com/volmit/iris/util/misc/E.java new file mode 100644 index 000000000..473fa2b17 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/misc/E.java @@ -0,0 +1,12 @@ +package com.volmit.iris.util.misc; + +public class E { + + public static > T getOrDefault(Class enumClass, String name, String fallback) { + try { + return Enum.valueOf(enumClass, name); + } catch (Throwable e) { + return Enum.valueOf(enumClass, fallback); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/misc/Hastebin.java b/core/src/main/java/com/volmit/iris/util/misc/Hastebin.java new file mode 100644 index 000000000..11e253b31 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/misc/Hastebin.java @@ -0,0 +1,135 @@ +package com.volmit.iris.util.misc; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import oshi.SystemInfo; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.file.Files; +import java.util.List; + +public class Hastebin { + + public static void enviornment(CommandSender sender) { + // Construct the server information + StringBuilder sb = new StringBuilder(); + SystemInfo systemInfo = new SystemInfo(); + KList disks = new KList<>(getHardware.getDisk()); + KList interfaces = new KList<>(getHardware.getInterfaces()); + KList displays = new KList<>(getHardware.getEDID()); + KList sensors = new KList<>(getHardware.getSensors()); + KList gpus = new KList<>(getHardware.getGraphicsCards()); + KList powersources = new KList<>(getHardware.getPowerSources()); + + KList IrisWorlds = new KList<>(); + KList BukkitWorlds = new KList<>(); + + for (World w : Bukkit.getServer().getWorlds()) { + try { + Engine engine = IrisToolbelt.access(w).getEngine(); + if (engine != null) { + IrisWorlds.add(w); + } + } catch (Exception e) { + BukkitWorlds.add(w); + } + } + + sb.append(" -- == Iris Info == -- \n"); + sb.append("Iris Version Version: ").append(Iris.instance.getDescription().getVersion()).append("\n"); + sb.append("- Iris Worlds"); + for (World w : IrisWorlds.copy()) { + sb.append(" - ").append(w.getName()); + } + sb.append("- Bukkit Worlds"); + for (World w : BukkitWorlds.copy()) { + sb.append(" - ").append(w.getName()); + } + sb.append(" -- == Platform Overview == -- " + "\n"); + sb.append("Server Type: ").append(Bukkit.getVersion()).append("\n"); + sb.append("Server Uptime: ").append(Form.stampTime(systemInfo.getOperatingSystem().getSystemUptime())).append("\n"); + sb.append("Version: ").append(Platform.getVersion()).append(" - Platform: ").append(Platform.getName()).append("\n"); + sb.append("Java Vendor: ").append(Platform.ENVIRONMENT.getJavaVendor()).append(" - Java Version: ").append(Platform.ENVIRONMENT.getJavaVersion()).append("\n"); + sb.append(" -- == Processor Overview == -- " + "\n"); + sb.append("CPU Model: ").append(getHardware.getCPUModel()); + sb.append("CPU Architecture: ").append(Platform.CPU.getArchitecture()).append(" Available Processors: ").append(Platform.CPU.getAvailableProcessors()).append("\n"); + sb.append("CPU Load: ").append(Form.pc(Platform.CPU.getCPULoad())).append(" CPU Live Process Load: ").append(Form.pc(Platform.CPU.getLiveProcessCPULoad())).append("\n"); + sb.append("-=" + " Graphics " + "=- " + "\n"); + for (String gpu : gpus) { + sb.append(" ").append(gpu).append("\n"); + } + sb.append(" -- == Memory Information == -- " + "\n"); + sb.append("Physical Memory - Total: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getUsedMemory())).append("\n"); + sb.append("Virtual Memory - Total: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getUsedMemory())).append("\n"); + sb.append(" -- == Storage Information == -- " + "\n"); + for (String disk : disks) { + sb.append(" ").append(sb.append(disk)).append("\n"); + } + sb.append(" -- == Interface Information == -- "+ "\n" ); + for (String inter : interfaces) { + sb.append(" ").append(inter).append("\n"); + } + sb.append(" -- == Display Information == -- "+ "\n" ); + for (String display : displays) { + sb.append(display).append("\n"); + } + sb.append(" -- == Sensor Information == -- " + "\n"); + for (String sensor : sensors) { + sb.append(" ").append(sensor).append("\n"); + } + sb.append(" -- == Power Information == -- " + "\n"); + for (String power : powersources) { + sb.append(" ").append(power).append("\n"); + } + + try { + String hastebinUrl = uploadToHastebin(sb.toString()); + + // Create the clickable message + TextComponent message = new TextComponent("[Link]"); + TextComponent link = new TextComponent(hastebinUrl); + link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, hastebinUrl)); + message.addExtra(link); + + // Send the clickable message to the player + sender.spigot().sendMessage(message); + } catch (Exception e) { + sender.sendMessage(C.DARK_RED + "Failed to upload server information to Hastebin."); + } + } + + private static String uploadToHastebin(String content) throws Exception { + URL url = new URL("https://paste.bytecode.ninja/documents"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "text/plain"); + conn.setDoOutput(true); + + DataOutputStream wr = new DataOutputStream(conn.getOutputStream()); + wr.writeBytes(content); + wr.flush(); + wr.close(); + + BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String response = br.readLine(); + br.close(); + + return "https://paste.bytecode.ninja/" + response.split("\"")[3]; + } + + +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/misc/Platform.java b/core/src/main/java/com/volmit/iris/util/misc/Platform.java new file mode 100644 index 000000000..afbf7c8c3 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/misc/Platform.java @@ -0,0 +1,145 @@ +package com.volmit.iris.util.misc; + +import com.sun.management.OperatingSystemMXBean; + +import java.io.File; +import java.lang.management.ManagementFactory; + +@SuppressWarnings("restriction") +public class Platform { + public static String getVersion() { + return getSystem().getVersion(); + } + + public static String getName() { + return getSystem().getName(); + } + + private static OperatingSystemMXBean getSystem() { + return (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + } + + public static class ENVIRONMENT { + public static boolean canRunBatch() { + return getSystem().getName().toLowerCase().contains("windows"); + } + + public static String getJavaHome() { + return System.getProperty("java.home"); + } + + public static String getJavaVendor() { + return System.getProperty("java.vendor"); + } + + public static String getJavaVersion() { + return System.getProperty("java.version"); + } + } + + public static class STORAGE { + public static long getAbsoluteTotalSpace() { + long t = 0; + + for (File i : getRoots()) { + t += getTotalSpace(i); + } + + return t; + } + + public static long getTotalSpace() { + return getTotalSpace(new File(".")); + } + + public static long getTotalSpace(File root) { + return root.getTotalSpace(); + } + + public static long getAbsoluteFreeSpace() { + long t = 0; + + for (File i : getRoots()) { + t += getFreeSpace(i); + } + + return t; + } + + public static long getFreeSpace() { + return getFreeSpace(new File(".")); + } + + public static long getFreeSpace(File root) { + return root.getFreeSpace(); + } + + public static long getUsedSpace() { + return getTotalSpace() - getFreeSpace(); + } + + public static long getUsedSpace(File root) { + return getTotalSpace(root) - getFreeSpace(root); + } + + public static long getAbsoluteUsedSpace() { + return getAbsoluteTotalSpace() - getAbsoluteFreeSpace(); + } + + public static File[] getRoots() { + return File.listRoots(); + } + } + + public static class MEMORY { + public static class PHYSICAL { + public static long getTotalMemory() { + return getSystem().getTotalPhysicalMemorySize(); + } + + public static long getFreeMemory() { + return getSystem().getFreePhysicalMemorySize(); + } + + public static long getUsedMemory() { + return getTotalMemory() - getFreeMemory(); + } + } + + public static class VIRTUAL { + public static long getTotalMemory() { + return getSystem().getTotalSwapSpaceSize(); + } + + public static long getFreeMemory() { + return getSystem().getFreeSwapSpaceSize(); + } + + public static long getUsedMemory() { + return getTotalMemory() - getFreeMemory(); + } + + public static long getCommittedVirtualMemory() { + return getSystem().getCommittedVirtualMemorySize(); + } + } + } + + public static class CPU { + public static int getAvailableProcessors() { + return getSystem().getAvailableProcessors(); + } + + public static double getCPULoad() { + return getSystem().getSystemCpuLoad(); + } + + public static double getLiveProcessCPULoad() { + return getSystem().getProcessCpuLoad(); + } + + public static String getArchitecture() { + return getSystem().getArch(); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/misc/getHardware.java b/core/src/main/java/com/volmit/iris/util/misc/getHardware.java index 0b9a52033..0e494e27c 100644 --- a/core/src/main/java/com/volmit/iris/util/misc/getHardware.java +++ b/core/src/main/java/com/volmit/iris/util/misc/getHardware.java @@ -1,11 +1,24 @@ package com.volmit.iris.util.misc; -import oshi.SystemInfo; -import oshi.hardware.CentralProcessor; -import oshi.hardware.HWDiskStore; -import oshi.software.os.OperatingSystem; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import org.jetbrains.annotations.Nullable; +import oshi.SystemInfo; +import oshi.hardware.*; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.EdidUtil; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; import java.util.List; +import java.util.stream.Collectors; public class getHardware { public static String getServerOS() { @@ -44,14 +57,127 @@ public class getHardware { } } - public static String getDiskModel() { - SystemInfo systemInfo = new SystemInfo(); - List diskStores = systemInfo.getHardware().getDiskStores(); - if (!diskStores.isEmpty()) { - HWDiskStore firstDisk = diskStores.get(0); - return firstDisk.getModel(); - } else { - return "Unknown Disk Model"; + public static KList getSensors() { + try { + KList temps = new KList<>(); + SystemInfo systemInfo = new SystemInfo(); + temps.add("CPU Temperature: " + systemInfo.getHardware().getSensors().getCpuTemperature()); + temps.add("CPU Voltage: " + systemInfo.getHardware().getSensors().getCpuTemperature()); + temps.add("Fan Speeds: " + Arrays.toString(systemInfo.getHardware().getSensors().getFanSpeeds())); + return temps.copy(); + } catch (Exception e) { + e.printStackTrace(); } + return null; } -} + + public static KList getGraphicsCards() { + try { + KList gpus = new KList<>(); + SystemInfo systemInfo = new SystemInfo(); + for (GraphicsCard gpu : systemInfo.getHardware().getGraphicsCards()) { + gpus.add(C.BLUE + "Gpu Model: " + C.GRAY + gpu.getName()); + gpus.add("- vRam Size: " + C.GRAY + Form.memSize(gpu.getVRam())); + gpus.add("- Vendor: " + C.GRAY + gpu.getVendor()); + } + return gpus.copy(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static KList getDisk() { + try { + KList systemDisks = new KList<>(); + SystemInfo systemInfo = new SystemInfo(); + List diskStores = systemInfo.getHardware().getDiskStores(); + OperatingSystem operatingSystem = systemInfo.getOperatingSystem(); + List fileStores = operatingSystem.getFileSystem().getFileStores(); + + for (HWDiskStore disk : diskStores) { + systemDisks.add(C.BLUE + "Disk: " + disk.getName()); + systemDisks.add("- Model: " + disk.getModel()); + systemDisks.add("Partitions: " + disk.getPartitions()); + for (OSFileStore partition : fileStores) { + systemDisks.add(C.BLUE + "- Name: " + partition.getName()); + systemDisks.add(" - Description: " + partition.getDescription()); + systemDisks.add(" - Total Space: " + Form.memSize(partition.getTotalSpace())); + systemDisks.add(" - Free Space: " + Form.memSize(partition.getFreeSpace())); + systemDisks.add(" - Mount: " + partition.getMount()); + systemDisks.add(" - Label: " + partition.getLabel()); + } + systemDisks.add(C.DARK_GRAY + "-=" + C.BLUE +" Since Boot " + C.DARK_GRAY + "=- "); + systemDisks.add("- Total Reads: " + Form.memSize(disk.getReadBytes())); + systemDisks.add("- Total Writes: " + Form.memSize(disk.getWriteBytes())); + } + if (systemDisks.isEmpty()) { + systemDisks.add("Failed to get disks."); + } + return systemDisks.copy(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static KList getPowerSources() { + try { + KList systemPowerSources = new KList<>(); + SystemInfo systemInfo = new SystemInfo(); + List powerSources = systemInfo.getHardware().getPowerSources(); + for (PowerSource powersource : powerSources) { + systemPowerSources.add(C.BLUE + "- Name: " + powersource.getName()); + systemPowerSources.add("- RemainingCapacityPercent: " + powersource.getRemainingCapacityPercent()); + systemPowerSources.add("- Power Usage Rate: " + powersource.getPowerUsageRate()); + systemPowerSources.add("- Power OnLine: " + powersource.isPowerOnLine()); + systemPowerSources.add("- Capacity Units: " + powersource.getCapacityUnits()); + systemPowerSources.add("- Cycle Count: " + powersource.getCycleCount()); + } + if (systemPowerSources.isEmpty()) { + systemPowerSources.add("No PowerSources."); + } + return systemPowerSources.copy(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static KList getEDID() { + try { + KList systemEDID = new KList<>(); + SystemInfo systemInfo = new SystemInfo(); + HardwareAbstractionLayer hardware = systemInfo.getHardware(); + List displays = hardware.getDisplays(); + for (Display display : displays) { + systemEDID.add("Display: " + display.getEdid()); + } + if (!systemEDID.isEmpty()) { + systemEDID.add("No displays"); + } + return systemEDID.copy(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static KList getInterfaces() { + try { + KList interfaces = new KList<>(); + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface ni : Collections.list(networkInterfaces)) { + interfaces.add(C.BLUE + "Display Name: %s", ni.getDisplayName()); + Enumeration inetAddresses = ni.getInetAddresses(); + for (InetAddress ia : Collections.list(inetAddresses)) { + interfaces.add("IP: %s", ia.getHostAddress()); + } + return interfaces.copy(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/nbt/mca/Chunk.java b/core/src/main/java/com/volmit/iris/util/nbt/mca/Chunk.java index 6437bbe0e..f74cdcf7f 100644 --- a/core/src/main/java/com/volmit/iris/util/nbt/mca/Chunk.java +++ b/core/src/main/java/com/volmit/iris/util/nbt/mca/Chunk.java @@ -27,10 +27,12 @@ import com.volmit.iris.util.nbt.io.NamedTag; import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.ListTag; +import org.bukkit.World; import java.io.*; import static com.volmit.iris.util.nbt.mca.LoadFlags.*; +import static org.bukkit.Bukkit.getServer; public class Chunk { public static final int DEFAULT_DATA_VERSION = 2730; @@ -39,6 +41,7 @@ public class Chunk { private int lastMCAUpdate; private CompoundTag data; private int dataVersion; + private int nativeIrisVersion; private long lastUpdate; private long inhabitedTime; private MCABiomeContainer biomes; @@ -71,15 +74,21 @@ public class Chunk { } public static Chunk newChunk() { + World mainWorld = getServer().getWorlds().get(0); Chunk c = new Chunk(0); c.dataVersion = DEFAULT_DATA_VERSION; c.data = new CompoundTag(); - c.biomes = INMS.get().newBiomeContainer(0, 256); + c.biomes = INMS.get().newBiomeContainer(mainWorld.getMinHeight(), mainWorld.getMaxHeight()); c.data.put("Level", defaultLevel()); c.status = "full"; return c; } + public static void injectIrisData(Chunk c) { + World mainWorld = getServer().getWorlds().get(0); + c.data.put("Iris", nativeIrisVersion()); + } + private static CompoundTag defaultLevel() { CompoundTag level = new CompoundTag(); level.putString("Status", "full"); @@ -87,19 +96,25 @@ public class Chunk { return level; } + private static CompoundTag nativeIrisVersion() { + CompoundTag level = new CompoundTag(); + level.putString("Generator", "Iris " + Iris.instance.getDescription().getVersion()); + return level; + } + private void initReferences(long loadFlags) { if (data == null) { throw new NullPointerException("data cannot be null"); } - CompoundTag level; - if ((level = data.getCompoundTag("Level")) == null) { - throw new IllegalArgumentException("data does not contain \"Level\" tag"); - } + + CompoundTag level = data; + + World mainWorld = getServer().getWorlds().get(0); dataVersion = data.getInt("DataVersion"); inhabitedTime = level.getLong("InhabitedTime"); lastUpdate = level.getLong("LastUpdate"); if ((loadFlags & BIOMES) != 0) { - biomes = INMS.get().newBiomeContainer(0, 256, level.getIntArray("Biomes")); + biomes = INMS.get().newBiomeContainer(mainWorld.getMinHeight(), mainWorld.getMaxHeight(), level.getIntArray("Biomes")); } if ((loadFlags & HEIGHTMAPS) != 0) { heightMaps = level.getCompoundTag("Heightmaps"); diff --git a/core/src/main/java/com/volmit/iris/util/plugin/Command.java b/core/src/main/java/com/volmit/iris/util/plugin/Command.java index bd9cd546c..c223c84b4 100644 --- a/core/src/main/java/com/volmit/iris/util/plugin/Command.java +++ b/core/src/main/java/com/volmit/iris/util/plugin/Command.java @@ -18,6 +18,8 @@ package com.volmit.iris.util.plugin; +import com.volmit.iris.util.format.C; + import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -28,4 +30,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target(FIELD) public @interface Command { String value() default ""; + } diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 83ad85017..a54d30829 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -12,7 +12,7 @@ libraries: - commons-io:commons-io:2.13.0 - io.timeandspace:smoothie-map:2.0.2 - com.google.guava:guava:31.0.1-jre - - com.google.code.gson:gson:2.8.9 + - com.google.code.gson:gson:2.10.1 - org.zeroturnaround:zt-zip:1.14 - it.unimi.dsi:fastutil:8.5.6 - org.ow2.asm:asm:9.2 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e5897b..48c0a02ca 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java index eec445bee..49c921288 100644 --- a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java @@ -1,5 +1,34 @@ package com.volmit.iris.core.nms.v1_19_R1; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_19_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_19_R1.entity.CraftDolphin; +import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; @@ -10,11 +39,13 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; + import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -24,40 +55,15 @@ import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.*; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; - -import org.bukkit.craftbukkit.v1_19_R1.CraftChunk; -import org.bukkit.craftbukkit.v1_19_R1.CraftServer; -import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock; -import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_19_R1.entity.CraftDolphin; -import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.Entity; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; import sun.misc.Unsafe; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); @@ -483,6 +489,34 @@ public class NMSBinding implements INMSBinding { biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); } + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java index bcdda2dea..8aa76bc52 100644 --- a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java @@ -1,36 +1,17 @@ package com.volmit.iris.core.nms.v1_19_R2; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.engine.data.cache.AtomicCache; -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.hunk.Hunk; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.io.NBTUtil; -import com.volmit.iris.util.nbt.mca.NBTWorld; -import com.volmit.iris.util.nbt.mca.palette.*; -import com.volmit.iris.util.nbt.tag.CompoundTag; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.TagParser; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunk; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -43,20 +24,46 @@ import org.bukkit.craftbukkit.v1_19_R2.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import sun.misc.Unsafe; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; +import com.volmit.iris.engine.data.cache.AtomicCache; +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.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.io.NBTUtil; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.TagParser; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -483,6 +490,35 @@ public class NMSBinding implements INMSBinding { biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); } + + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java index f11974faf..c32354ae9 100644 --- a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java @@ -1,5 +1,34 @@ package com.volmit.iris.core.nms.v1_19_R3; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_19_R3.CraftChunk; +import org.bukkit.craftbukkit.v1_19_R3.CraftServer; +import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock; +import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftDolphin; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; @@ -10,11 +39,13 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; + import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -25,6 +56,7 @@ import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -32,34 +64,8 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.*; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; - -import org.bukkit.craftbukkit.v1_19_R3.CraftChunk; -import org.bukkit.craftbukkit.v1_19_R3.CraftServer; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock; -import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_19_R3.entity.CraftDolphin; -import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.Entity; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; import sun.misc.Unsafe; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); @@ -276,7 +282,7 @@ public class NMSBinding implements INMSBinding { @Override public KList getBiomes() { - return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM); + return new KList<>(Biome.values()).qdel(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM); } @Override @@ -486,6 +492,37 @@ public class NMSBinding implements INMSBinding { biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); } + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + if (type == org.bukkit.entity.EntityType.CAMEL) { + return null; + } + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index 02169ff3f..165f97808 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -10,6 +10,7 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; @@ -25,6 +26,7 @@ import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -45,6 +47,9 @@ import org.bukkit.craftbukkit.v1_20_R1.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.entity.EntityType; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -56,8 +61,10 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.List; +import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { @@ -474,6 +481,34 @@ public class NMSBinding implements INMSBinding { cd.getHandle().setGotFish(true); } + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = net.minecraft.world.entity.EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(net.minecraft.world.entity.EntityType.class)) { + try { + net.minecraft.world.entity.EntityType entityType = (net.minecraft.world.entity.EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { ServerLevel serverLevel = ((CraftWorld)world).getHandle(); Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index 1f02cf3ab..73a15d89c 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -1,5 +1,34 @@ package com.volmit.iris.core.nms.v1_20_R2; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_20_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_20_R2.CraftServer; +import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R2.entity.CraftDolphin; +import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; @@ -10,11 +39,13 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; + import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -26,6 +57,7 @@ import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -33,34 +65,8 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.*; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; - -import org.bukkit.craftbukkit.v1_20_R2.CraftChunk; -import org.bukkit.craftbukkit.v1_20_R2.CraftServer; -import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_20_R2.entity.CraftDolphin; -import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; -import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.Entity; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; import sun.misc.Unsafe; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); @@ -486,6 +492,34 @@ public class NMSBinding implements INMSBinding { biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); } + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 167400d19..01fa75d94 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -1,5 +1,34 @@ package com.volmit.iris.core.nms.v1_20_R3; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_20_R3.CraftChunk; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftDolphin; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; @@ -10,11 +39,13 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; + import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -26,6 +57,7 @@ import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; @@ -33,34 +65,8 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.*; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; - -import org.bukkit.craftbukkit.v1_20_R3.CraftChunk; -import org.bukkit.craftbukkit.v1_20_R3.CraftServer; -import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_20_R3.entity.CraftDolphin; -import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; -import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.Entity; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; import sun.misc.Unsafe; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); @@ -486,6 +492,35 @@ public class NMSBinding implements INMSBinding { biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); } + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java new file mode 100644 index 000000000..d37284c25 --- /dev/null +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java @@ -0,0 +1,168 @@ +package com.volmit.iris.core.nms.v1_20_R4; + +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.math.RNG; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R4.CraftServer; +import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class CustomBiomeSource extends BiomeSource { + + private final long seed; + private final Engine engine; + private final Registry biomeCustomRegistry; + private final Registry biomeRegistry; + private final AtomicCache registryAccess = new AtomicCache<>(); + private final RNG rng; + private final KMap> customBiomes; + + public CustomBiomeSource(long seed, Engine engine, World world) { + this.engine = engine; + this.seed = seed; + this.biomeCustomRegistry = registry().registry(Registries.BIOME).orElse(null); + this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).registry(Registries.BIOME).orElse(null); + this.rng = new RNG(engine.getSeedManager().getBiome()); + this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine); + } + + private static List> getAllBiomes(Registry customRegistry, Registry registry, Engine engine) { + List> b = new ArrayList<>(); + + for (IrisBiome i : engine.getAllBiomes()) { + if (i.isCustom()) { + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + b.add(customRegistry.getHolder(customRegistry.getResourceKey(customRegistry + .get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get()); + } + } else { + b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative())); + } + } + + return b; + } + + private static Object getFor(Class type, Object source) { + Object o = fieldFor(type, source); + + if (o != null) { + return o; + } + + return invokeFor(type, source); + } + + private static Object fieldFor(Class returns, Object in) { + return fieldForClass(returns, in.getClass(), in); + } + + private static Object invokeFor(Class returns, Object in) { + for (Method i : in.getClass().getMethods()) { + if (i.getReturnType().equals(returns)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); + return i.invoke(in); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + return null; + } + + @SuppressWarnings("unchecked") + private static T fieldForClass(Class returnType, Class sourceType, Object in) { + for (Field i : sourceType.getDeclaredFields()) { + if (i.getType().equals(returnType)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); + return (T) i.get(in); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return null; + } + + @Override + protected Stream> collectPossibleBiomes() { + return getAllBiomes( + ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) + .registry(Registries.BIOME).orElse(null), + ((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().registry(Registries.BIOME).orElse(null), + engine).stream(); + } + private KMap> fillCustomBiomes(Registry customRegistry, Engine engine) { + KMap> m = new KMap<>(); + + for (IrisBiome i : engine.getAllBiomes()) { + if (i.isCustom()) { + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + ResourceLocation resourceLocation = new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()); + Biome biome = customRegistry.get(resourceLocation); + Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); + if (optionalBiomeKey.isEmpty()) { + Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName()); + continue; + } + ResourceKey biomeKey = optionalBiomeKey.get(); + Optional> optionalReferenceHolder = customRegistry.getHolder(biomeKey); + if (optionalReferenceHolder.isEmpty()) { + Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName()); + continue; + } + m.put(j.getId(), optionalReferenceHolder.get()); + } + } + } + + return m; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + @Override + protected MapCodec codec() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { + int m = (y - engine.getMinHeight()) << 2; + IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2); + if (ib.isCustom()) { + return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId()); + } else { + org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2); + return NMSBinding.biomeToBiomeBase(biomeRegistry, v); + } + } +} \ No newline at end of file diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java new file mode 100644 index 000000000..59f90620b --- /dev/null +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -0,0 +1,552 @@ +package com.volmit.iris.core.nms.v1_20_R4; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + +import com.volmit.iris.core.nms.datapack.DataVersion; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_20_R4.CraftChunk; +import org.bukkit.craftbukkit.v1_20_R4.CraftServer; +import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; +import com.volmit.iris.engine.data.cache.AtomicCache; +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.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.io.NBTUtil; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.TagParser; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import sun.misc.Unsafe; + +public class NMSBinding implements INMSBinding { + private final KMap baseBiomeCache = new KMap<>(); + private final BlockData AIR = Material.AIR.createBlockData(); + private final AtomicCache> biomeMapCache = new AtomicCache<>(); + private final AtomicCache> registryCache = new AtomicCache<>(); + private final AtomicCache> globalCache = new AtomicCache<>(); + private final AtomicCache registryAccess = new AtomicCache<>(); + private final AtomicCache byIdRef = new AtomicCache<>(); + private Field biomeStorageCache = null; + + private static Object getFor(Class type, Object source) { + Object o = fieldFor(type, source); + + if (o != null) { + return o; + } + + return invokeFor(type, source); + } + + private static Object invokeFor(Class returns, Object in) { + for (Method i : in.getClass().getMethods()) { + if (i.getReturnType().equals(returns)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); + return i.invoke(in); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + return null; + } + + private static Object fieldFor(Class returns, Object in) { + return fieldForClass(returns, in.getClass(), in); + } + + @SuppressWarnings("unchecked") + private static T fieldForClass(Class returnType, Class sourceType, Object in) { + for (Field i : sourceType.getDeclaredFields()) { + if (i.getType().equals(returnType)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); + return (T) i.get(in); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return null; + } + + private static Class getClassType(Class type, int ordinal) { + return type.getDeclaredClasses()[ordinal]; + } + + @Override + public boolean hasTile(Location l) { + return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null; + } + + @Override + public CompoundTag serializeTile(Location location) { + BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), true); + + if (e == null) { + return null; + } + + net.minecraft.nbt.CompoundTag tag = e.saveWithFullMetadata(registry()); + return convert(tag); + } + + private CompoundTag convert(net.minecraft.nbt.CompoundTag tag) { + try { + ByteArrayOutputStream boas = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(boas); + tag.write(dos); + dos.close(); + return (CompoundTag) NBTUtil.read(new ByteArrayInputStream(boas.toByteArray()), false).getTag(); + } catch (Throwable ex) { + ex.printStackTrace(); + } + + return null; + } + + private net.minecraft.nbt.CompoundTag convert(CompoundTag tag) { + try { + ByteArrayOutputStream boas = new ByteArrayOutputStream(); + NBTUtil.write(tag, boas, false); + DataInputStream din = new DataInputStream(new ByteArrayInputStream(boas.toByteArray())); + net.minecraft.nbt.CompoundTag c = NbtIo.read(din); + din.close(); + return c; + } catch (Throwable e) { + e.printStackTrace(); + } + + return null; + } + + @Override + public void deserializeTile(CompoundTag c, Location pos) { + ((CraftWorld) pos.getWorld()).getHandle().getChunkAt(new BlockPos(pos.getBlockX(), 0, pos.getBlockZ())).setBlockEntityNbt(convert(c)); + } + + @Override + public CompoundTag serializeEntity(Entity location) { + return null;// TODO: + } + + @Override + public Entity deserializeEntity(CompoundTag s, Location newPosition) { + return null;// TODO: + } + + @Override + public boolean supportsCustomHeight() { + return true; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + private Registry getCustomBiomeRegistry() { + return registry().registry(Registries.BIOME).orElse(null); + } + + private Registry getBlockRegistry() { + return registry().registry(Registries.BLOCK).orElse(null); + } + + @Override + public Object getBiomeBaseFromId(int id) { + return getCustomBiomeRegistry().getHolder(id); + } + + @Override + public int getMinHeight(World world) { + return world.getMinHeight(); + } + + @Override + public boolean supportsCustomBiomes() { + return true; + } + + @Override + public int getTrueBiomeBaseId(Object biomeBase) { + return getCustomBiomeRegistry().getId(((Holder) biomeBase).value()); + } + + @Override + public Object getTrueBiomeBase(Location location) { + return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); + } + + @Override + public String getTrueBiomeBaseKey(Location location) { + return getKeyForBiomeBase(getTrueBiomeBase(location)); + } + + @Override + public Object getCustomBiomeBaseFor(String mckey) { + return getCustomBiomeRegistry().get(new ResourceLocation(mckey)); + } + + @Override + public Object getCustomBiomeBaseHolderFor(String mckey) { + return getCustomBiomeRegistry().getHolder(getTrueBiomeBaseId(getCustomBiomeRegistry().get(new ResourceLocation(mckey)))).get(); + } + + public int getBiomeBaseIdForKey(String key) { + return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(new ResourceLocation(key))); + } + + @Override + public String getKeyForBiomeBase(Object biomeBase) { + return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something + } + + @Override + public Object getBiomeBase(World world, Biome biome) { + return biomeToBiomeBase(((CraftWorld) world).getHandle() + .registryAccess().registry(Registries.BIOME).orElse(null), biome); + } + + @Override + public Object getBiomeBase(Object registry, Biome biome) { + Object v = baseBiomeCache.get(biome); + + if (v != null) { + return v; + } + //noinspection unchecked + v = biomeToBiomeBase((Registry) registry, biome); + if (v == null) { + // Ok so there is this new biome name called "CUSTOM" in Paper's new releases. + // But, this does NOT exist within CraftBukkit which makes it return an error. + // So, we will just return the ID that the plains biome returns instead. + //noinspection unchecked + return biomeToBiomeBase((Registry) registry, Biome.PLAINS); + } + baseBiomeCache.put(biome, v); + return v; + } + + @Override + public KList getBiomes() { + return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM); + } + + @Override + public boolean isBukkit() { + return true; + } + + @Override + public int getBiomeId(Biome biome) { + for (World i : Bukkit.getWorlds()) { + if (i.getEnvironment().equals(World.Environment.NORMAL)) { + Registry registry = ((CraftWorld) i).getHandle().registryAccess().registry(Registries.BIOME).orElse(null); + return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome)); + } + } + + return biome.ordinal(); + } + + private MCAIdMap getBiomeMapping() { + return biomeMapCache.aquire(() -> new MCAIdMap<>() { + @NotNull + @Override + public Iterator iterator() { + return getCustomBiomeRegistry().iterator(); + } + + @Override + public int getId(net.minecraft.world.level.biome.Biome paramT) { + return getCustomBiomeRegistry().getId(paramT); + } + + @Override + public net.minecraft.world.level.biome.Biome byId(int paramInt) { + return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt); + } + }); + } + + @NotNull + private MCABiomeContainer getBiomeContainerInterface(MCAIdMap biomeMapping, MCAChunkBiomeContainer base) { + return new MCABiomeContainer() { + @Override + public int[] getData() { + return base.writeBiomes(); + } + + @Override + public void setBiome(int x, int y, int z, int id) { + base.setBiome(x, y, z, biomeMapping.byId(id)); + } + + @Override + public int getBiome(int x, int y, int z) { + return biomeMapping.getId(base.getBiome(x, y, z)); + } + }; + } + + @Override + public MCABiomeContainer newBiomeContainer(int min, int max) { + MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max); + return getBiomeContainerInterface(getBiomeMapping(), base); + } + + @Override + public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) { + MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data); + return getBiomeContainerInterface(getBiomeMapping(), base); + } + + @Override + public int countCustomBiomes() { + AtomicInteger a = new AtomicInteger(0); + + getCustomBiomeRegistry().keySet().forEach((i) -> { + if (i.getNamespace().equals("minecraft")) { + return; + } + + a.incrementAndGet(); + Iris.debug("Custom Biome: " + i); + }); + + return a.get(); + } + + public boolean supportsDataPacks() { + return true; + } + + public void setBiomes(int cx, int cz, World world, Hunk biomes) { + LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz); + biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder) b)); + c.setUnsaved(true); + } + + @Override + public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) { + try { + ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk); + Holder biome = (Holder) somethingVeryDirty; + s.setBiome(x, y, z, biome); + } catch (IllegalAccessException e) { + Iris.reportError(e); + e.printStackTrace(); + } + } + + private Field getFieldForBiomeStorage(Object storage) { + Field f = biomeStorageCache; + + if (f != null) { + return f; + } + try { + f = storage.getClass().getDeclaredField("biome"); + f.setAccessible(true); + return f; + } catch (Throwable e) { + Iris.reportError(e); + e.printStackTrace(); + Iris.error(storage.getClass().getCanonicalName()); + } + + biomeStorageCache = f; + return null; + } + + @Override + public MCAPaletteAccess createPalette() { + MCAIdMapper registry = registryCache.aquireNasty(() -> { + Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId"); + Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT"); + Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId"); + cf.setAccessible(true); + df.setAccessible(true); + bf.setAccessible(true); + net.minecraft.core.IdMapper blockData = Block.BLOCK_STATE_REGISTRY; + int b = bf.getInt(blockData); + Object2IntMap c = (Object2IntMap) cf.get(blockData); + List d = (List) df.get(blockData); + return new MCAIdMapper(c, d, b); + }); + MCAPalette global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState())); + MCAPalettedContainer container = new MCAPalettedContainer<>(global, registry, + i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(), + i -> NBTWorld.getCompound(CraftBlockData.fromData(i)), + ((CraftBlockData) AIR).getState()); + return new MCAWrappedPalettedContainer<>(container, + i -> NBTWorld.getCompound(CraftBlockData.fromData(i)), + i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState()); + } + + @Override + public void injectBiomesFromMantle(Chunk e, Mantle mantle) { + ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL); + AtomicInteger c = new AtomicInteger(); + AtomicInteger r = new AtomicInteger(); + mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> { + if (b != null) { + if (b.isCustom()) { + chunk.setBiome(x, y, z, getCustomBiomeRegistry().getHolder(b.getBiomeId()).get()); + c.getAndIncrement(); + } else { + chunk.setBiome(x, y, z, (Holder) getBiomeBase(e.getWorld(), b.getBiome())); + r.getAndIncrement(); + } + } + }); + } + + public ItemStack applyCustomNbt(ItemStack itemStack, KMap customNbt) throws IllegalArgumentException { + if (customNbt != null && !customNbt.isEmpty()) { + net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack); + + try { + net.minecraft.nbt.CompoundTag tag = TagParser.parseTag((new JSONObject(customNbt)).toString()); + tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe()); + s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } catch (CommandSyntaxException var5) { + throw new IllegalArgumentException(var5); + } + + return CraftItemStack.asBukkitCopy(s); + } else { + return itemStack; + } + } + + public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { + CraftDolphin cd = (CraftDolphin)dolphin; + cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); + cd.getHandle().setGotFish(true); + } + + public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { + ServerLevel serverLevel = ((CraftWorld)world).getHandle(); + Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); + Field biomeSource = getField(clazz, BiomeSource.class); + biomeSource.setAccessible(true); + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe)unsafeField.get(null); + CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); + unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); + biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + } + + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { + try { + for (Field f : clazz.getDeclaredFields()) { + if (f.getType().equals(fieldType)) + return f; + } + throw new NoSuchFieldException(fieldType.getName()); + } catch (NoSuchFieldException var4) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw var4; + } else { + return getField(superClass, fieldType); + } + } + } + + public static Holder biomeToBiomeBase(Registry registry, Biome biome) { + return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); + } + + @Override + public DataVersion getDataVersion() { + return DataVersion.V1205; + } +} diff --git a/settings.gradle b/settings.gradle index ba41bb67e..c4b3fa275 100644 --- a/settings.gradle +++ b/settings.gradle @@ -26,6 +26,7 @@ rootProject.name = 'Iris' include(':core') include( + ':nms:v1_20_R4', ':nms:v1_20_R3', ':nms:v1_20_R2', ':nms:v1_20_R1',