9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-19 15:09:18 +00:00

Compare commits

...

44 Commits

Author SHA1 Message Date
Aidan Aeternum
93ca26e368 v+ 2025-12-17 12:12:25 -05:00
Aidan Aeternum
8c1db1c223 Merge pull request #1231 from VolmitSoftware/dev
3.8.0
2025-12-17 12:11:28 -05:00
Julian Krings
123708601f improve pregen cache 2025-12-01 16:01:54 +01:00
Julian Krings
b5ab4968ba fix compile 2025-11-30 15:36:36 +01:00
Julian Krings
93ae6037bd whoops forgot to disable craftbukkit relocations on <=1.20.4 2025-11-29 11:49:15 +01:00
Julian Krings
dd8e487a3b fix tile entity serialization on paper 1.21.6+ servers 2025-11-29 11:13:30 +01:00
Julian Krings
d3c8377a12 allow switching between buildtools and userdev 2025-11-29 11:12:16 +01:00
Julian Krings
84f3687c0c update compat for iron bars 2025-11-27 16:09:31 +01:00
Julian Krings
ed1e1f1181 whoops forgot those providers 2025-11-26 21:33:03 +01:00
Julian Krings
528c97f367 replace IrisCustomData class with a proxy to keep full access to the base BlockData 2025-11-26 21:32:13 +01:00
Julian Krings
f7a459f3bc initial 1.21.9/10 bindings 2025-11-26 21:25:05 +01:00
Julian Krings
cd179b4321 reintroduce native threads for the updater as a setting and cleanup 2025-11-26 20:55:48 +01:00
Julian Krings
6373dbb1b8 potential optimization for the noise cache 2025-11-21 16:21:54 +01:00
Julian Krings
0b0797f876 minor optimization for chunk updater 2025-11-21 16:21:31 +01:00
Julian Krings
446acefc91 minor optimization for the mantle writer 2025-11-21 16:21:02 +01:00
Julian Krings
7a44e555b2 rename noise cache size setting and decrease default value 2025-11-21 16:20:32 +01:00
Julian Krings
57d4c2935c add cache sizes to engine status 2025-11-21 16:16:15 +01:00
Julian Krings
234fb1b0c4 also include parent class for schema generation 2025-11-08 00:58:12 +01:00
Julian Krings
0882b5acc4 use parent shared classloader to reflect intelij behavior 2025-11-06 22:32:28 +01:00
Julian Krings
18da26e1fa minor performance improvement 2025-11-06 22:27:14 +01:00
Julian Krings
65aa95f2a5 fix wrong radius when marking mantle chunks as completed 2025-11-06 22:25:37 +01:00
Julian Krings
5330ddc4ec fix deleting object ids with mantle cleanup 2025-11-06 22:20:30 +01:00
Julian Krings
a7fdd37569 fix studio loot command 2025-11-06 22:16:27 +01:00
Julian Krings
a226fea9e2 fix regen command 2025-11-06 16:22:02 +01:00
Julian Krings
8cea165a29 even more performance improvements 2025-11-06 15:29:03 +01:00
Julian Krings
1d7cba184c fix linear palette not growing correctly 2025-11-06 14:41:39 +01:00
Julian Krings
4ca7ea3911 minor speed improvements 2025-11-01 22:36:25 +01:00
Julian Krings
ea5919def2 stop writing first access each time the engine is opened 2025-10-30 16:40:35 +01:00
Julian Krings
be35e49302 use coroutines for mantle generation 2025-10-30 16:40:08 +01:00
Julian Krings
aadd03990a optimize data palette for mantle slices 2025-10-27 19:53:47 +01:00
Julian Krings
38a579453d optimize noise cache 2025-10-26 13:49:03 +01:00
Julian Krings
0bf5da2ca1 optimize object maps 2025-10-26 13:48:11 +01:00
Julian Krings
317848692e improve regen speed 2025-10-06 14:12:01 +02:00
Julian Krings
22118de9e9 fix object smart bore 2025-10-06 14:10:37 +02:00
Julian Krings
d7039d120b fix place commands causing unwanted block updates 2025-10-06 13:00:00 +02:00
Julian Krings
979ee4e7d8 switch hashing algorithm for objects once more 2025-10-05 21:46:12 +02:00
Julian Krings
f68d45bd30 dynamically resolve snippet classes 2025-10-05 00:21:38 +02:00
Julian Krings
b86d7f303e restructure the shared kts classloader to be more consistent 2025-10-05 00:20:55 +02:00
Julian Krings
c573843314 fix kts dependency resolver 2025-10-05 00:17:57 +02:00
Julian Krings
51a7bef18e whoops forgot escaping the path 2025-10-04 17:19:11 +02:00
Julian Krings
0e237aa1ad fix generated build.gradle.kts on external dives on windows 2025-10-04 13:41:48 +02:00
Julian Krings
b46c413f6b make gradle setup print to console on failure 2025-10-04 13:40:47 +02:00
Julian Krings
f3ef1ca2ae fix typo in preprocessors description 2025-10-04 13:40:07 +02:00
Julian Krings
703e61dd54 fix preprocessors not applying reliably 2025-10-04 13:39:33 +02:00
86 changed files with 4170 additions and 1735 deletions

View File

@@ -1,5 +1,3 @@
import com.volmit.nmstools.NMSToolsExtension
import com.volmit.nmstools.NMSToolsPlugin
import de.undercouch.gradle.tasks.download.Download import de.undercouch.gradle.tasks.download.Download
import xyz.jpenilla.runpaper.task.RunServer import xyz.jpenilla.runpaper.task.RunServer
import kotlin.system.exitProcess import kotlin.system.exitProcess
@@ -30,13 +28,12 @@ buildscript {
plugins { plugins {
java java
`java-library` `java-library`
alias(libs.plugins.shadow)
alias(libs.plugins.download) alias(libs.plugins.download)
alias(libs.plugins.runPaper) alias(libs.plugins.runPaper)
} }
group = "com.volmit" group = "com.volmit"
version = "3.7.11-1.20.1-1.21.8" version = "3.8.0-1.20.1-1.21.10"
apply<ApiGenerator>() apply<ApiGenerator>()
@@ -58,13 +55,15 @@ registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins")
registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins") registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins")
// ============================================================== // ==============================================================
val serverMinHeap = "2G" val serverMinHeap = "10G"
val serverMaxHeap = "8G" val serverMaxHeap = "10G"
val additionalFlags = "-XX:+AlwaysPreTouch"
//Valid values are: none, truecolor, indexed256, indexed16, indexed8 //Valid values are: none, truecolor, indexed256, indexed16, indexed8
val color = "truecolor" val color = "truecolor"
val errorReporting = findProperty("errorReporting") as Boolean? ?: false val errorReporting = findProperty("errorReporting") as Boolean? ?: false
val nmsBindings = mapOf( val nmsBindings = mapOf(
"v1_21_R6" to "1.21.10-R0.1-SNAPSHOT",
"v1_21_R5" to "1.21.8-R0.1-SNAPSHOT", "v1_21_R5" to "1.21.8-R0.1-SNAPSHOT",
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT", "v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT", "v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
@@ -76,14 +75,14 @@ val nmsBindings = mapOf(
"v1_20_R1" to "1.20.1-R0.1-SNAPSHOT", "v1_20_R1" to "1.20.1-R0.1-SNAPSHOT",
) )
val jvmVersion = mapOf<String, Int>() val jvmVersion = mapOf<String, Int>()
nmsBindings.forEach { key, value -> nmsBindings.forEach { (key, value) ->
project(":nms:$key") { project(":nms:$key") {
apply<JavaPlugin>() apply<JavaPlugin>()
apply<NMSToolsPlugin>()
extensions.configure(NMSToolsExtension::class) { nmsBinding {
jvm = jvmVersion.getOrDefault(key, 21) jvm = jvmVersion.getOrDefault(key, 21)
version = value version = value
type = NMSBinding.Type.DIRECT
} }
dependencies { dependencies {
@@ -106,17 +105,23 @@ nmsBindings.forEach { key, value ->
systemProperty("com.mojang.eula.agree", true) systemProperty("com.mojang.eula.agree", true)
systemProperty("iris.suppressReporting", !errorReporting) systemProperty("iris.suppressReporting", !errorReporting)
jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}") jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}")
jvmArgs(additionalFlags.split(' '))
} }
} }
val jarJar: Configuration by configurations.creating
dependencies {
for (key in nmsBindings.keys) {
implementation(project(":nms:$key", "reobf"))
}
implementation(project(":core", "shadow"))
jarJar(project(":core:agent"))
}
tasks { tasks {
jar { jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
nmsBindings.forEach { key, _ -> from(jarJar, configurations.runtimeClasspath.map { it.resolve().map(::zipTree) })
from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) })
}
from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) })
from(project(":core:agent").tasks.jar.flatMap { it.archiveFile })
archiveFileName.set("Iris-${project.version}.jar") archiveFileName.set("Iris-${project.version}.jar")
} }

View File

@@ -1,11 +1,16 @@
plugins { plugins {
kotlin("jvm") version "2.0.20" kotlin("jvm") version embeddedKotlinVersion
} }
repositories { repositories {
mavenCentral() mavenCentral()
gradlePluginPortal()
maven("https://jitpack.io")
} }
dependencies { dependencies {
implementation("org.ow2.asm:asm:9.8") implementation("org.ow2.asm:asm:9.8")
implementation("com.github.VolmitSoftware:NMSTools:c88961416f")
implementation("io.papermc.paperweight:paperweight-userdev:2.0.0-beta.18")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
} }

View File

@@ -0,0 +1,182 @@
import NMSBinding.Type
import com.volmit.nmstools.NMSToolsExtension
import com.volmit.nmstools.NMSToolsPlugin
import io.papermc.paperweight.userdev.PaperweightUser
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
import io.papermc.paperweight.userdev.PaperweightUserExtension
import io.papermc.paperweight.userdev.attribute.Obfuscation
import io.papermc.paperweight.util.constants.REOBF_CONFIG
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.gradle.api.*
import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.TaskAction
import org.gradle.internal.extensions.core.extra
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.work.DisableCachingByDefault
import java.io.RandomAccessFile
import javax.inject.Inject
class NMSBinding : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
val config = extra["nms"] as? Config ?: throw GradleException("No NMS binding configuration found")
val jvm = config.jvm
val type = config.type
if (type == Type.USER_DEV) {
plugins.apply(PaperweightUser::class.java)
dependencies.extensions.findByType(PaperweightUserDependenciesExtension::class.java)
?.paperDevBundle(config.version)
val java = extensions.findByType(JavaPluginExtension::class.java) ?: throw GradleException("Java plugin not found")
java.toolchain.languageVersion.set(JavaLanguageVersion.of(jvm))
val javaToolchains = project.extensions.getByType(JavaToolchainService::class.java) ?: throw GradleException("Java toolchain service not found")
extensions.configure(PaperweightUserExtension::class.java) {
it.javaLauncher.set(javaToolchains.launcherFor(java.toolchain))
}
} else {
extra["nmsTools.useBuildTools"] = type == Type.BUILD_TOOLS
plugins.apply(NMSToolsPlugin::class.java)
extensions.configure(NMSToolsExtension::class.java) {
it.jvm.set(jvm)
it.version.set(config.version)
}
configurations.register(REOBF_CONFIG) { conf ->
conf.isCanBeConsumed = true
conf.isCanBeResolved = false
conf.attributes {
it.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
it.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
it.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
it.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
it.attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED))
}
conf.outgoing.artifact(tasks.named("remap"))
}
}
val (major, minor) = config.version.parseVersion()
if (major <= 20 && minor <= 4) return@with
tasks.register("convert", ConversionTask::class.java, type)
tasks.named("compileJava") { it.dependsOn("convert") }
rootProject.tasks.named("prepareKotlinBuildScriptModel") { it.dependsOn("$path:convert") }
}
@DisableCachingByDefault
abstract class ConversionTask @Inject constructor(type: Type) : DefaultTask() {
private val pattern: Regex
private val replacement: String
init {
group = "nms"
inputs.property("type", type)
val java = project.extensions.findByType(JavaPluginExtension::class.java) ?: throw GradleException("Java plugin not found")
val source = java.sourceSets.named("main").map { it.allJava }
inputs.files(source)
outputs.files(source)
if (type == Type.USER_DEV) {
pattern = "org\\.bukkit\\.craftbukkit\\.${project.name}".toRegex()
replacement = "org.bukkit.craftbukkit"
} else {
pattern = "org\\.bukkit\\.craftbukkit\\.(?!${project.name})".toRegex()
replacement = "org.bukkit.craftbukkit.${project.name}."
}
}
@TaskAction
fun process() {
val dispatcher = Dispatchers.IO.limitedParallelism(16)
runBlocking {
for (file in inputs.files) {
if (file.extension !in listOf("java"))
continue
launch(dispatcher) {
val output = ArrayList<String>()
var changed = false
file.bufferedReader().use {
for (line in it.lines()) {
if (line.startsWith("package") || line.isBlank()) {
output += line
continue
}
if (!line.startsWith("import")) {
if (!changed) return@launch
else {
output += line
continue
}
}
if (!line.contains(pattern)) {
output += line
continue
}
output += line.replace(pattern, replacement)
changed = true
}
}
if (changed) {
RandomAccessFile(file, "r").use { raf ->
val bytes = ByteArray(NEW_LINE_BYTES.size)
raf.seek(raf.length() - bytes.size)
raf.readFully(bytes)
if (bytes.contentEquals(NEW_LINE_BYTES))
output += ""
}
file.writer().use {
val iterator = output.iterator()
while (iterator.hasNext()) {
it.append(iterator.next())
if (iterator.hasNext())
it.append(NEW_LINE)
}
}
}
}
}
}
}
}
enum class Type {
USER_DEV,
BUILD_TOOLS,
DIRECT,
}
}
private val NEW_LINE = System.lineSeparator()
private val NEW_LINE_BYTES = NEW_LINE.encodeToByteArray()
private fun String.parseVersion() = substringBefore('-').split(".").let {
it[1].toInt() to it[2].toInt()
}
class Config(
var jvm: Int = 21,
var type: Type = Type.DIRECT
) {
lateinit var version: String
}
fun Project.nmsBinding(action: Config.() -> Unit) {
extra["nms"] = Config().apply(action)
plugins.apply(NMSBinding::class.java)
}
private inline fun <reified T : Named> ObjectFactory.named(name: String): T = named(T::class.java, name)

View File

@@ -142,6 +142,7 @@ slimJar {
relocate("com.google.inject", "$lib.guice") relocate("com.google.inject", "$lib.guice")
relocate("org.dom4j", "$lib.dom4j") relocate("org.dom4j", "$lib.dom4j")
relocate("org.jaxen", "$lib.jaxen") relocate("org.jaxen", "$lib.jaxen")
relocate("com.github.benmanes.caffeine", "$lib.caffeine")
} }
tasks { tasks {

View File

@@ -58,6 +58,7 @@ import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.plugin.VolmitPlugin; import com.volmit.iris.util.plugin.VolmitPlugin;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.plugin.chunk.ChunkTickets;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.Queue;
import com.volmit.iris.util.scheduling.ShurikenQueue; import com.volmit.iris.util.scheduling.ShurikenQueue;
@@ -95,6 +96,7 @@ public class Iris extends VolmitPlugin implements Listener {
public static MultiverseCoreLink linkMultiverseCore; public static MultiverseCoreLink linkMultiverseCore;
public static IrisCompat compat; public static IrisCompat compat;
public static FileWatcher configWatcher; public static FileWatcher configWatcher;
public static ChunkTickets tickets;
private static VolmitSender sender; private static VolmitSender sender;
private static Thread shutdownHook; private static Thread shutdownHook;
@@ -450,6 +452,7 @@ public class Iris extends VolmitPlugin implements Listener {
IrisSafeguard.IrisSafeguardSystem(); IrisSafeguard.IrisSafeguardSystem();
getSender().setTag(getTag()); getSender().setTag(getTag());
IrisSafeguard.splash(true); IrisSafeguard.splash(true);
tickets = new ChunkTickets();
linkMultiverseCore = new MultiverseCoreLink(); linkMultiverseCore = new MultiverseCoreLink();
configWatcher = new FileWatcher(getDataFile("settings.json")); configWatcher = new FileWatcher(getDataFile("settings.json"));
services.values().forEach(IrisService::onEnable); services.values().forEach(IrisService::onEnable);

View File

@@ -159,7 +159,7 @@ public class IrisSettings {
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC(); private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
public boolean trimMantleInStudio = false; public boolean trimMantleInStudio = false;
public int mantleKeepAlive = 30; public int mantleKeepAlive = 30;
public int cacheSize = 4_096; public int noiseCacheSize = 1_024;
public int resourceLoaderCacheSize = 1_024; public int resourceLoaderCacheSize = 1_024;
public int objectLoaderCacheSize = 4_096; public int objectLoaderCacheSize = 4_096;
public int scriptLoaderCacheSize = 512; public int scriptLoaderCacheSize = 512;
@@ -177,6 +177,9 @@ public class IrisSettings {
@Data @Data
public static class IrisSettingsUpdater { public static class IrisSettingsUpdater {
public int maxConcurrency = 256; public int maxConcurrency = 256;
public boolean nativeThreads = false;
public double threadMultiplier = 2;
public double chunkLoadSensitivity = 0.7; public double chunkLoadSensitivity = 0.7;
public MsRange emptyMsRange = new MsRange(80, 100); public MsRange emptyMsRange = new MsRange(80, 100);
public MsRange defaultMsRange = new MsRange(20, 40); public MsRange defaultMsRange = new MsRange(20, 40);
@@ -185,6 +188,10 @@ public class IrisSettings {
return Math.max(Math.abs(maxConcurrency), 1); return Math.max(Math.abs(maxConcurrency), 1);
} }
public double getThreadMultiplier() {
return Math.min(Math.abs(threadMultiplier), 0.1);
}
public double getChunkLoadSensitivity() { public double getChunkLoadSensitivity() {
return Math.min(chunkLoadSensitivity, 0.9); return Math.min(chunkLoadSensitivity, 0.9);
} }
@@ -243,6 +250,7 @@ public class IrisSettings {
public int maxBiomeChildDepth = 4; public int maxBiomeChildDepth = 4;
public boolean preventLeafDecay = true; public boolean preventLeafDecay = true;
public boolean useMulticore = false; public boolean useMulticore = false;
public boolean useMulticoreMantle = false;
public boolean offsetNoiseTypes = false; public boolean offsetNoiseTypes = false;
public boolean earlyCustomBlocks = false; public boolean earlyCustomBlocks = false;
} }

View File

@@ -38,7 +38,6 @@ import com.volmit.iris.util.decree.specialhandlers.ObjectHandler;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.Direction; import com.volmit.iris.util.math.Direction;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.Queue;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@@ -79,9 +78,9 @@ public class CommandObject implements DecreeExecutor {
futureBlockChanges.put(block, block.getBlockData()); futureBlockChanges.put(block, block.getBlockData());
if (d instanceof IrisCustomData data) { if (d instanceof IrisCustomData data) {
block.setBlockData(data.getBase()); block.setBlockData(data.getBase(), false);
Iris.warn("Tried to place custom block at " + x + ", " + y + ", " + z + " which is not supported!"); Iris.warn("Tried to place custom block at " + x + ", " + y + ", " + z + " which is not supported!");
} else block.setBlockData(d); } else block.setBlockData(d, false);
} }
@Override @Override
@@ -124,6 +123,16 @@ public class CommandObject implements DecreeExecutor {
tile.toBukkitTry(world.getBlockAt(xx, yy, zz)); tile.toBukkitTry(world.getBlockAt(xx, yy, zz));
} }
@Override
public <T> void setData(int xx, int yy, int zz, T data) {
}
@Override
public <T> T getData(int xx, int yy, int zz, Class<T> t) {
return null;
}
@Override @Override
public Engine getEngine() { public Engine getEngine() {
return null; return null;
@@ -140,7 +149,7 @@ public class CommandObject implements DecreeExecutor {
sender().sendMessage("Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD() + ""); sender().sendMessage("Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD() + "");
sender().sendMessage("Blocks Used: " + NumberFormat.getIntegerInstance().format(o.getBlocks().size())); sender().sendMessage("Blocks Used: " + NumberFormat.getIntegerInstance().format(o.getBlocks().size()));
Queue<BlockData> queue = o.getBlocks().enqueueValues(); var queue = o.getBlocks().values();
Map<Material, Set<BlockData>> unsorted = new HashMap<>(); Map<Material, Set<BlockData>> unsorted = new HashMap<>();
Map<BlockData, Integer> amounts = new HashMap<>(); Map<BlockData, Integer> amounts = new HashMap<>();
Map<Material, Integer> materials = new HashMap<>(); Map<Material, Integer> materials = new HashMap<>();

View File

@@ -58,7 +58,7 @@ import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.O; import com.volmit.iris.util.scheduling.O;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.scheduling.jobs.ParallelQueueJob; import com.volmit.iris.util.scheduling.jobs.ParallelRadiusJob;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
@@ -78,6 +78,7 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -174,56 +175,49 @@ public class CommandStudio implements DecreeExecutor {
PlatformChunkGenerator plat = IrisToolbelt.access(world); PlatformChunkGenerator plat = IrisToolbelt.access(world);
Engine engine = plat.getEngine(); Engine engine = plat.getEngine();
DecreeContext.touch(sender); DecreeContext.touch(sender);
try (SyncExecutor executor = new SyncExecutor(20)) { try (SyncExecutor executor = new SyncExecutor(20);
var service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
) {
int x = loc.getBlockX() >> 4; int x = loc.getBlockX() >> 4;
int z = loc.getBlockZ() >> 4; int z = loc.getBlockZ() >> 4;
int rad = engine.getMantle().getRadius(); int rad = engine.getMantle().getRadius();
var mantle = engine.getMantle().getMantle(); var mantle = engine.getMantle().getMantle();
var chunkMap = new KMap<Position2, MantleChunk>(); var chunkMap = new KMap<Position2, MantleChunk>();
ParallelQueueJob<Position2> prep = new ParallelQueueJob<>() { ParallelRadiusJob prep = new ParallelRadiusJob(Integer.MAX_VALUE, service) {
@Override @Override
public void execute(Position2 pos) { protected void execute(int rX, int rZ) {
var cpos = pos.add(x, z); if (Math.abs(rX) <= radius && Math.abs(rZ) <= radius) {
if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) { mantle.deleteChunk(rX + x, rZ + z);
mantle.deleteChunk(cpos.getX(), cpos.getZ());
return; return;
} }
chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ())); rX += x;
mantle.deleteChunk(cpos.getX(), cpos.getZ()); rZ += z;
chunkMap.put(new Position2(rX, rZ), mantle.getChunk(rX, rZ));
mantle.deleteChunk(rX, rZ);
} }
@Override @Override
public String getName() { public String getName() {
return "Preparing Mantle"; return "Preparing Mantle";
} }
}; }.retarget(radius + rad, 0, 0);
for (int xx = -(radius + rad); xx <= radius + rad; xx++) {
for (int zz = -(radius + rad); zz <= radius + rad; zz++) {
prep.queue(new Position2(xx, zz));
}
}
CountDownLatch pLatch = new CountDownLatch(1); CountDownLatch pLatch = new CountDownLatch(1);
prep.execute(sender(), pLatch::countDown); prep.execute(sender(), pLatch::countDown);
pLatch.await(); pLatch.await();
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() { ParallelRadiusJob job = new ParallelRadiusJob(Integer.MAX_VALUE, service) {
@Override @Override
public void execute(Position2 p) { protected void execute(int x, int z) {
plat.injectChunkReplacement(world, p.getX(), p.getZ(), executor); plat.injectChunkReplacement(world, x, z, executor);
} }
@Override @Override
public String getName() { public String getName() {
return "Regenerating"; return "Regenerating";
} }
}; }.retarget(radius, x, z);
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
job.queue(new Position2(i + x, j + z));
}
}
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
job.execute(sender(), latch::countDown); job.execute(sender(), latch::countDown);
latch.await(); latch.await();
@@ -339,11 +333,15 @@ public class CommandStudio implements DecreeExecutor {
O<Integer> ta = new O<>(); O<Integer> ta = new O<>();
ta.set(-1); ta.set(-1);
var sender = sender();
var player = player();
var engine = engine();
ta.set(Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () -> ta.set(Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () ->
{ {
if (!player().getOpenInventory().getType().equals(InventoryType.CHEST)) { if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
Bukkit.getScheduler().cancelTask(ta.get()); Bukkit.getScheduler().cancelTask(ta.get());
sender().sendMessage(C.GREEN + "Opened inventory!"); sender.sendMessage(C.GREEN + "Opened inventory!");
return; return;
} }
@@ -351,7 +349,7 @@ public class CommandStudio implements DecreeExecutor {
inv.clear(); inv.clear();
} }
engine().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1); engine.addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ(), 1);
}, 0, fast ? 5 : 35)); }, 0, fast ? 5 : 35));
sender().sendMessage(C.GREEN + "Opening inventory now!"); sender().sendMessage(C.GREEN + "Opening inventory now!");

View File

@@ -67,7 +67,7 @@ public class HMCLeavesDataProvider extends ExternalDataProvider {
BlockData blockData = Bukkit.createBlockData(material); BlockData blockData = Bukkit.createBlockData(material);
if (IrisSettings.get().getGenerator().preventLeafDecay && blockData instanceof Leaves leaves) if (IrisSettings.get().getGenerator().preventLeafDecay && blockData instanceof Leaves leaves)
leaves.setPersistent(true); leaves.setPersistent(true);
return new IrisCustomData(blockData, ExternalDataSVC.buildState(blockId, state)); return IrisCustomData.of(blockData, ExternalDataSVC.buildState(blockId, state));
} }
@NotNull @NotNull

View File

@@ -47,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
if (block == null) { if (block == null) {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
} }
return new IrisCustomData(block.getBaseBlockData(), blockId); return IrisCustomData.of(block.getBaseBlockData(), blockId);
} }
@NotNull @NotNull

View File

@@ -33,7 +33,7 @@ public class KGeneratorsDataProvider extends ExternalDataProvider {
@Override @Override
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) throws MissingResourceException { public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) throws MissingResourceException {
if (Main.getGenerators().get(blockId.key()) == null) throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); if (Main.getGenerators().get(blockId.key()) == null) throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
return new IrisCustomData(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state)); return IrisCustomData.of(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state));
} }
@Override @Override

View File

@@ -72,7 +72,7 @@ public class MythicCrucibleDataProvider extends ExternalDataProvider {
CustomBlockItemContext blockItemContext = crucibleItem.getBlockData(); CustomBlockItemContext blockItemContext = crucibleItem.getBlockData();
FurnitureItemContext furnitureItemContext = crucibleItem.getFurnitureData(); FurnitureItemContext furnitureItemContext = crucibleItem.getFurnitureData();
if (furnitureItemContext != null) { if (furnitureItemContext != null) {
return new IrisCustomData(B.getAir(), ExternalDataSVC.buildState(blockId, state)); return IrisCustomData.of(B.getAir(), ExternalDataSVC.buildState(blockId, state));
} else if (blockItemContext != null) { } else if (blockItemContext != null) {
return blockItemContext.getBlockData(); return blockItemContext.getBlockData();
} }

View File

@@ -49,9 +49,9 @@ public class NexoDataProvider extends ExternalDataProvider {
BlockData data = NexoBlocks.blockData(blockId.key()); BlockData data = NexoBlocks.blockData(blockId.key());
if (data == null) if (data == null)
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
return new IrisCustomData(data, blockState); return IrisCustomData.of(data, blockState);
} else if (NexoFurniture.isFurniture(blockId.key())) { } else if (NexoFurniture.isFurniture(blockId.key())) {
return new IrisCustomData(B.getAir(), blockState); return IrisCustomData.of(B.getAir(), blockState);
} }
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());

View File

@@ -50,16 +50,15 @@ import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Objects; import java.util.*;
import java.util.Optional;
@Data @Data
public class IrisData implements ExclusionStrategy, TypeAdapterFactory { public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
private static final KMap<File, IrisData> dataLoaders = new KMap<>(); private static final KMap<File, IrisData> dataLoaders = new KMap<>();
private final File dataFolder; private final File dataFolder;
private final int id; private final int id;
private final PackEnvironment environment;
private boolean closed = false; private boolean closed = false;
private PackEnvironment environment;
private ResourceLoader<IrisBiome> biomeLoader; private ResourceLoader<IrisBiome> biomeLoader;
private ResourceLoader<IrisLootTable> lootLoader; private ResourceLoader<IrisLootTable> lootLoader;
private ResourceLoader<IrisRegion> regionLoader; private ResourceLoader<IrisRegion> regionLoader;
@@ -92,7 +91,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.engine = null; this.engine = null;
this.dataFolder = dataFolder; this.dataFolder = dataFolder;
this.id = RNG.r.imax(); this.id = RNG.r.imax();
this.environment = PackEnvironment.create(this);
hotloaded(); hotloaded();
} }
@@ -350,7 +348,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
public synchronized void hotloaded() { public synchronized void hotloaded() {
closed = false; closed = false;
environment.close();
possibleSnippets = new KMap<>(); possibleSnippets = new KMap<>();
builder = new GsonBuilder() builder = new GsonBuilder()
.addDeserializationExclusionStrategy(this) .addDeserializationExclusionStrategy(this)
@@ -382,6 +379,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.imageLoader = registerLoader(IrisImage.class); this.imageLoader = registerLoader(IrisImage.class);
this.scriptLoader = registerLoader(IrisScript.class); this.scriptLoader = registerLoader(IrisScript.class);
this.matterObjectLoader = registerLoader(IrisMatterObject.class); this.matterObjectLoader = registerLoader(IrisMatterObject.class);
this.environment = PackEnvironment.create(this);
builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter); builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
gson = builder.create(); gson = builder.create();
@@ -389,6 +387,10 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
.map(IrisDimension::getDataScripts) .map(IrisDimension::getDataScripts)
.flatMap(KList::stream) .flatMap(KList::stream)
.forEach(environment::execute); .forEach(environment::execute);
if (engine != null) {
engine.hotload();
}
} }
public void dump() { public void dump() {
@@ -404,6 +406,33 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
possibleSnippets.clear(); possibleSnippets.clear();
} }
public Set<Class<?>> resolveSnippets() {
var result = new HashSet<Class<?>>();
var processed = new HashSet<Class<?>>();
var excluder = gson.excluder();
var queue = new LinkedList<Class<?>>(loaders.keySet());
while (!queue.isEmpty()) {
var type = queue.poll();
if (excluder.excludeClass(type, false) || !processed.add(type))
continue;
if (type.isAnnotationPresent(Snippet.class))
result.add(type);
try {
for (var field : type.getDeclaredFields()) {
if (excluder.excludeField(field, false))
continue;
queue.add(field.getType());
}
} catch (Throwable ignored) {
}
}
return result;
}
public String toLoadKey(File f) { public String toLoadKey(File f) {
if (f.getPath().startsWith(getDataFolder().getPath())) { if (f.getPath().startsWith(getDataFolder().getPath())) {
String[] full = f.getPath().split("\\Q" + File.separator + "\\E"); String[] full = f.getPath().split("\\Q" + File.separator + "\\E");

View File

@@ -35,7 +35,7 @@ import java.io.File;
@Data @Data
public abstract class IrisRegistrant { public abstract class IrisRegistrant {
@Desc("Preprocess this object in-memory when it's loaded, run scripts using the variable 'object' and modify properties about this object before it's used.\nFile extension: .prox.kts") @Desc("Preprocess this object in-memory when it's loaded, run scripts using the variable 'object' and modify properties about this object before it's used.\nFile extension: .proc.kts")
@RegistryListResource(IrisScript.class) @RegistryListResource(IrisScript.class)
@ArrayType(min = 1, type = String.class) @ArrayType(min = 1, type = String.class)
private KList<String> preprocessors = new KList<>(); private KList<String> preprocessors = new KList<>();

View File

@@ -46,6 +46,7 @@ import lombok.ToString;
import java.io.*; import java.io.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -170,7 +171,6 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return possibleKeys; return possibleKeys;
} }
KSet<String> m = new KSet<>();
KList<File> files = getFolders(); KList<File> files = getFolders();
if (files == null) { if (files == null) {
@@ -178,6 +178,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return possibleKeys; return possibleKeys;
} }
HashSet<String> m = new HashSet<>();
for (File i : files) { for (File i : files) {
for (File j : matchAllFiles(i, (f) -> f.getName().endsWith(".json"))) { for (File j : matchAllFiles(i, (f) -> f.getName().endsWith(".json"))) {
m.add(i.toURI().relativize(j.toURI()).getPath().replaceAll("\\Q.json\\E", "")); m.add(i.toURI().relativize(j.toURI()).getPath().replaceAll("\\Q.json\\E", ""));
@@ -319,7 +320,8 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return null; return null;
} }
firstAccess.add(name); var set = firstAccess;
if (set != null) firstAccess.add(name);
return loadCache.get(name); return loadCache.get(name);
} }
@@ -342,21 +344,24 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
} }
din.close(); din.close();
file.deleteOnExit();
Iris.info("Loading " + s.size() + " prefetch " + getFolderName()); Iris.info("Loading " + s.size() + " prefetch " + getFolderName());
firstAccess = null;
loadAllParallel(s); loadAllParallel(s);
} }
public void saveFirstAccess(Engine engine) throws IOException { public void saveFirstAccess(Engine engine) throws IOException {
if (firstAccess == null) return;
String id = "DIM" + Math.abs(engine.getSeedManager().getSeed() + engine.getDimension().getVersion() + engine.getDimension().getLoadKey().hashCode()); String id = "DIM" + Math.abs(engine.getSeedManager().getSeed() + engine.getDimension().getVersion() + engine.getDimension().getLoadKey().hashCode());
File file = Iris.instance.getDataFile("prefetch/" + id + "/" + Math.abs(getFolderName().hashCode()) + ".ipfch"); File file = Iris.instance.getDataFile("prefetch/" + id + "/" + Math.abs(getFolderName().hashCode()) + ".ipfch");
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file); FileOutputStream fos = new FileOutputStream(file);
GZIPOutputStream gzo = new CustomOutputStream(fos, 9); GZIPOutputStream gzo = new CustomOutputStream(fos, 9);
DataOutputStream dos = new DataOutputStream(gzo); DataOutputStream dos = new DataOutputStream(gzo);
dos.writeInt(firstAccess.size()); var set = firstAccess;
firstAccess = null;
dos.writeInt(set.size());
for (String i : firstAccess) { for (String i : set) {
dos.writeUTF(i); dos.writeUTF(i);
} }

View File

@@ -28,9 +28,10 @@ import java.util.List;
public class INMS { public class INMS {
private static final Version CURRENT = Boolean.getBoolean("iris.no-version-limit") ? private static final Version CURRENT = Boolean.getBoolean("iris.no-version-limit") ?
new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, null) : new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, null) :
new Version(21, 8, null); new Version(21, 10, null);
private static final List<Version> REVISION = List.of( private static final List<Version> REVISION = List.of(
new Version(21, 9, "v1_21_R6"),
new Version(21, 6, "v1_21_R5"), new Version(21, 6, "v1_21_R5"),
new Version(21, 5, "v1_21_R4"), new Version(21, 5, "v1_21_R4"),
new Version(21, 4, "v1_21_R3"), new Version(21, 4, "v1_21_R3"),

View File

@@ -2,16 +2,15 @@ package com.volmit.iris.core.pregenerator;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.core.tools.IrisToolbelt; 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.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RollingSequence; import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.plugin.chunk.TicketHolder;
import com.volmit.iris.util.profile.LoadBalancer; import com.volmit.iris.util.profile.LoadBalancer;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
@@ -21,7 +20,6 @@ import org.bukkit.World;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -31,7 +29,7 @@ public class ChunkUpdater {
private static final String REGION_PATH = "region" + File.separator + "r."; private static final String REGION_PATH = "region" + File.separator + "r.";
private final AtomicBoolean paused = new AtomicBoolean(); private final AtomicBoolean paused = new AtomicBoolean();
private final AtomicBoolean cancelled = new AtomicBoolean(); private final AtomicBoolean cancelled = new AtomicBoolean();
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>(); private final TicketHolder holder;
private final RollingSequence chunksPerSecond = new RollingSequence(5); private final RollingSequence chunksPerSecond = new RollingSequence(5);
private final AtomicInteger totalMaxChunks = new AtomicInteger(); private final AtomicInteger totalMaxChunks = new AtomicInteger();
private final AtomicInteger chunksProcessed = new AtomicInteger(); private final AtomicInteger chunksProcessed = new AtomicInteger();
@@ -40,13 +38,13 @@ public class ChunkUpdater {
private final AtomicBoolean serverEmpty = new AtomicBoolean(true); private final AtomicBoolean serverEmpty = new AtomicBoolean(true);
private final AtomicLong lastCpsTime = new AtomicLong(M.ms()); private final AtomicLong lastCpsTime = new AtomicLong(M.ms());
private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency(); private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency();
private final int coreLimit = (int) Math.max(Runtime.getRuntime().availableProcessors() * IrisSettings.get().getUpdater().getThreadMultiplier(), 1);
private final Semaphore semaphore = new Semaphore(maxConcurrency); private final Semaphore semaphore = new Semaphore(maxConcurrency);
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange); private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange);
private final AtomicLong startTime = new AtomicLong(); private final AtomicLong startTime = new AtomicLong();
private final Dimensions dimensions; private final Dimensions dimensions;
private final PregenTask task; private final PregenTask task;
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); private final ExecutorService chunkExecutor = IrisSettings.get().getUpdater().isNativeThreads() ? Executors.newFixedThreadPool(coreLimit) : Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final CountDownLatch latch; private final CountDownLatch latch;
private final Engine engine; private final Engine engine;
@@ -55,6 +53,7 @@ public class ChunkUpdater {
public ChunkUpdater(World world) { public ChunkUpdater(World world) {
this.engine = IrisToolbelt.access(world).getEngine(); this.engine = IrisToolbelt.access(world).getEngine();
this.world = world; this.world = world;
this.holder = Iris.tickets.getHolder(world);
this.dimensions = calculateWorldDimensions(new File(world.getWorldFolder(), "region")); this.dimensions = calculateWorldDimensions(new File(world.getWorldFolder(), "region"));
this.task = dimensions.task(); this.task = dimensions.task();
this.totalMaxChunks.set(dimensions.count * 1024); this.totalMaxChunks.set(dimensions.count * 1024);
@@ -113,7 +112,6 @@ public class ChunkUpdater {
e.printStackTrace(); e.printStackTrace();
} }
}, 0, 3, TimeUnit.SECONDS); }, 0, 3, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(this::unloadChunks, 0, 1, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(() -> { scheduler.scheduleAtFixedRate(() -> {
boolean empty = Bukkit.getOnlinePlayers().isEmpty(); boolean empty = Bukkit.getOnlinePlayers().isEmpty();
if (serverEmpty.getAndSet(empty) == empty) if (serverEmpty.getAndSet(empty) == empty)
@@ -128,6 +126,7 @@ public class ChunkUpdater {
t.setPriority(Thread.MAX_PRIORITY); t.setPriority(Thread.MAX_PRIORITY);
t.start(); t.start();
Iris.service(PreservationSVC.class).register(t);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -140,8 +139,6 @@ public class ChunkUpdater {
chunkExecutor.shutdown(); chunkExecutor.shutdown();
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS); chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
scheduler.shutdownNow(); scheduler.shutdownNow();
unloadAndSaveAllChunks(); unloadAndSaveAllChunks();
} catch (Exception ignored) {} } catch (Exception ignored) {}
@@ -200,20 +197,16 @@ public class ChunkUpdater {
return; return;
} }
var mc = engine.getMantle().getMantle().getChunk(x, z).use();
try { try {
Chunk c = world.getChunkAt(x, z); Chunk c = world.getChunkAt(x, z);
engine.getMantle().getMantle().getChunk(c);
engine.updateChunk(c); engine.updateChunk(c);
for (int xx = -1; xx <= 1; xx++) { removeTickets(x, z);
for (int zz = -1; zz <= 1; zz++) {
var counter = lastUse.get(Cache.key(x + xx, z + zz));
if (counter != null) counter.getB().decrementAndGet();
}
}
} finally { } finally {
chunksUpdated.incrementAndGet(); chunksUpdated.incrementAndGet();
chunksProcessed.getAndIncrement(); chunksProcessed.getAndIncrement();
mc.release();
} }
} }
@@ -235,40 +228,15 @@ public class ChunkUpdater {
for (int dz = -1; dz <= 1; dz++) { for (int dz = -1; dz <= 1; dz++) {
int xx = x + dx; int xx = x + dx;
int zz = z + dz; int zz = z + dz;
executor.submit(() -> { PaperLib.getChunkAtAsync(world, xx, zz, false, true)
try { .thenAccept(chunk -> {
Chunk c; if (chunk == null || !chunk.isGenerated()) {
try {
c = PaperLib.getChunkAtAsync(world, xx, zz, false, true)
.thenApply(chunk -> {
if (chunk != null)
chunk.addPluginChunkTicket(Iris.instance);
return chunk;
}).get();
} catch (InterruptedException | ExecutionException e) {
generated.set(false);
return;
}
if (c == null) {
generated.set(false);
return;
}
if (!c.isLoaded()) {
var future = J.sfut(() -> c.load(false));
if (future != null) future.join();
}
if (!PaperLib.isChunkGenerated(c.getWorld(), xx, zz))
generated.set(false);
var pair = lastUse.computeIfAbsent(Cache.key(c), k -> new Pair<>(0L, new AtomicInteger(-1)));
pair.setA(M.ms());
pair.getB().updateAndGet(i -> i == -1 ? 1 : ++i);
} finally {
latch.countDown(); latch.countDown();
generated.set(false);
return;
} }
holder.addTicket(chunk);
latch.countDown();
}); });
} }
} }
@@ -278,27 +246,16 @@ public class ChunkUpdater {
} catch (InterruptedException e) { } catch (InterruptedException e) {
Iris.info("Interrupted while waiting for chunks to load"); Iris.info("Interrupted while waiting for chunks to load");
} }
return generated.get();
if (generated.get()) return true;
removeTickets(x, z);
return false;
} }
private synchronized void unloadChunks() { private void removeTickets(int x, int z) {
for (var key : new ArrayList<>(lastUse.keySet())) { for (int xx = -1; xx <= 1; xx++) {
if (key == null) continue; for (int zz = -1; zz <= 1; zz++) {
var pair = lastUse.get(key); holder.removeTicket(x + xx, z + zz);
if (pair == null) continue;
var lastUseTime = pair.getA();
var counter = pair.getB();
if (lastUseTime == null || counter == null)
continue;
if (M.ms() - lastUseTime >= 5000 && counter.get() == 0) {
int x = Cache.keyX(key);
int z = Cache.keyZ(key);
J.s(() -> {
world.removePluginChunkTicket(x, z, Iris.instance);
world.unloadChunk(x, z);
lastUse.remove(key);
});
} }
} }
} }
@@ -311,7 +268,6 @@ public class ChunkUpdater {
return; return;
} }
unloadChunks();
world.save(); world.save();
}).get(); }).get();
} catch (Throwable e) { } catch (Throwable e) {

View File

@@ -24,9 +24,11 @@ public interface PregenCache {
void write(); void write();
void trim(long unloadDuration);
static PregenCache create(File directory) { static PregenCache create(File directory) {
if (directory == null) return EMPTY; if (directory == null) return EMPTY;
return new PregenCacheImpl(directory); return new PregenCacheImpl(directory, 16);
} }
default PregenCache sync() { default PregenCache sync() {
@@ -51,19 +53,16 @@ public interface PregenCache {
} }
@Override @Override
public void cacheChunk(int x, int z) { public void cacheChunk(int x, int z) {}
}
@Override @Override
public void cacheRegion(int x, int z) { public void cacheRegion(int x, int z) {}
}
@Override @Override
public void write() { public void write() {}
} @Override
public void trim(long unloadDuration) {}
}; };

View File

@@ -1,220 +0,0 @@
package com.volmit.iris.core.pregenerator.cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.volmit.iris.Iris;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.parallel.HyperLock;
import lombok.RequiredArgsConstructor;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@RequiredArgsConstructor
class PregenCacheImpl implements PregenCache {
private static final int SIZE = 32;
private final File directory;
private final HyperLock hyperLock = new HyperLock(SIZE * 2, true);
private final LoadingCache<Pos, Plate> cache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS)
.executor(KCache.EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.maximumSize(SIZE)
.removalListener(this::onRemoval)
.evictionListener(this::onRemoval)
.build(this::load);
@ChunkCoordinates
public boolean isChunkCached(int x, int z) {
var plate = cache.get(new Pos(x >> 10, z >> 10));
if (plate == null) return false;
return plate.isCached((x >> 5) & 31, (z >> 5) & 31, r -> r.isCached(x & 31, z & 31));
}
@RegionCoordinates
public boolean isRegionCached(int x, int z) {
var plate = cache.get(new Pos(x >> 5, z >> 5));
if (plate == null) return false;
return plate.isCached(x & 31, z & 31, Region::isCached);
}
@ChunkCoordinates
public void cacheChunk(int x, int z) {
var plate = cache.get(new Pos(x >> 10, z >> 10));
plate.cache((x >> 5) & 31, (z >> 5) & 31, r -> r.cache(x & 31, z & 31));
}
@RegionCoordinates
public void cacheRegion(int x, int z) {
var plate = cache.get(new Pos(x >> 5, z >> 5));
plate.cache(x & 31, z & 31, Region::cache);
}
public void write() {
cache.asMap().values().forEach(this::write);
}
private Plate load(Pos key) {
hyperLock.lock(key.x, key.z);
try {
File file = fileForPlate(key);
if (!file.exists()) return new Plate(key);
try (var in = new DataInputStream(new LZ4BlockInputStream(new FileInputStream(file)))) {
return new Plate(key, in);
} catch (IOException e){
Iris.error("Failed to read pregen cache " + file);
Iris.reportError(e);
e.printStackTrace();
return new Plate(key);
}
} finally {
hyperLock.unlock(key.x, key.z);
}
}
private void write(Plate plate) {
hyperLock.lock(plate.pos.x, plate.pos.z);
try {
File file = fileForPlate(plate.pos);
try {
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), plate::write);
} catch (IOException e) {
Iris.error("Failed to write pregen cache " + file);
Iris.reportError(e);
e.printStackTrace();
}
} finally {
hyperLock.unlock(plate.pos.x, plate.pos.z);
}
}
private void onRemoval(@Nullable Pos key, @Nullable Plate plate, RemovalCause cause) {
if (plate == null) return;
write(plate);
}
private File fileForPlate(Pos pos) {
if (!directory.exists() && !directory.mkdirs())
throw new IllegalStateException("Cannot create directory: " + directory.getAbsolutePath());
return new File(directory, "c." + pos.x + "." + pos.z + ".lz4b");
}
private static class Plate {
private final Pos pos;
private short count;
private Region[] regions;
public Plate(Pos pos) {
this.pos = pos;
count = 0;
regions = new Region[1024];
}
public Plate(Pos pos, DataInput in) throws IOException {
this.pos = pos;
count = (short) Varint.readSignedVarInt(in);
if (count == 1024) return;
regions = new Region[1024];
for (int i = 0; i < 1024; i++) {
if (in.readBoolean()) continue;
regions[i] = new Region(in);
}
}
public boolean isCached(int x, int z, Predicate<Region> predicate) {
if (count == 1024) return true;
Region region = regions[x * 32 + z];
if (region == null) return false;
return predicate.test(region);
}
public void cache(int x, int z, Predicate<Region> predicate) {
if (count == 1024) return;
Region region = regions[x * 32 + z];
if (region == null) regions[x * 32 + z] = region = new Region();
if (predicate.test(region)) count++;
}
public void write(DataOutput out) throws IOException {
Varint.writeSignedVarInt(count, out);
if (count == 1024) return;
for (Region region : regions) {
out.writeBoolean(region == null);
if (region == null) continue;
region.write(out);
}
}
}
private static class Region {
private short count;
private long[] words;
public Region() {
count = 0;
words = new long[64];
}
public Region(DataInput in) throws IOException {
count = (short) Varint.readSignedVarInt(in);
if (count == 1024) return;
words = new long[64];
for (int i = 0; i < 64; i++) {
words[i] = Varint.readUnsignedVarLong(in);
}
}
public boolean cache() {
if (count == 1024) return false;
count = 1024;
words = null;
return true;
}
public boolean cache(int x, int z) {
if (count == 1024) return false;
int i = x * 32 + z;
int w = i >> 6;
long b = 1L << (i & 63);
var cur = (words[w] & b) != 0;
if (cur) return false;
if (++count == 1024) {
words = null;
return true;
} else words[w] |= b;
return false;
}
public boolean isCached() {
return count == 1024;
}
public boolean isCached(int x, int z) {
int i = x * 32 + z;
return count == 1024 || (words[i >> 6] & 1L << (i & 63)) != 0;
}
public void write(DataOutput out) throws IOException {
Varint.writeSignedVarInt(count, out);
if (isCached()) return;
for (long word : words) {
Varint.writeUnsignedVarLong(word, out);
}
}
}
private record Pos(int x, int z) {}
}

View File

@@ -1,11 +1,6 @@
package com.volmit.iris.core.pregenerator.cache; package com.volmit.iris.core.pregenerator.cache;
import lombok.AllArgsConstructor; record SynchronizedCache(PregenCache cache) implements PregenCache {
@AllArgsConstructor
class SynchronizedCache implements PregenCache {
private final PregenCache cache;
@Override @Override
public boolean isThreadSafe() { public boolean isThreadSafe() {
return true; return true;
@@ -45,4 +40,11 @@ class SynchronizedCache implements PregenCache {
cache.write(); cache.write();
} }
} }
@Override
public void trim(long unloadDuration) {
synchronized (cache) {
cache.trim(unloadDuration);
}
}
} }

View File

@@ -9,9 +9,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.util.HashMap; import java.util.*;
import java.util.Optional;
import java.util.Scanner;
public class Gradle { public class Gradle {
private static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().contains("win"); private static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
@@ -38,10 +36,15 @@ public class Gradle {
cmd[0] = gradle.getAbsolutePath(); cmd[0] = gradle.getAbsolutePath();
System.arraycopy(args, 0, cmd, 1, args.length); System.arraycopy(args, 0, cmd, 1, args.length);
var process = Runtime.getRuntime().exec(cmd, ENVIRONMENT, projectDir); var process = Runtime.getRuntime().exec(cmd, ENVIRONMENT, projectDir);
attach(process.getInputStream()); var lines = Collections.synchronizedList(new ArrayList<String>());
attach(process.getErrorStream()); attach(process.getInputStream(), lines);
attach(process.getErrorStream(), lines);
var code = process.waitFor(); var code = process.waitFor();
if (code == 0) return; if (code == 0) {
lines.forEach(Iris::debug);
return;
}
lines.forEach(Iris::error);
throw new RuntimeException("Gradle exited with code " + code); throw new RuntimeException("Gradle exited with code " + code);
} }
@@ -91,12 +94,12 @@ public class Gradle {
.orElseThrow(() -> new RuntimeException("Failed to find java home, please set java.home system property")); .orElseThrow(() -> new RuntimeException("Failed to find java home, please set java.home system property"));
} }
private static void attach(InputStream stream) { private static void attach(InputStream stream, List<String> list) {
Thread.ofVirtual().start(() -> { Thread.ofPlatform().start(() -> {
try (var in = new Scanner(stream)) { try (var in = new Scanner(stream)) {
while (in.hasNextLine()) { while (in.hasNextLine()) {
String line = in.nextLine(); String line = in.nextLine();
Iris.debug("[GRADLE] " + line); list.add(line);
} }
} }
}); });

View File

@@ -24,7 +24,6 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.loader.ResourceLoader; import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.scripting.environment.SimpleEnvironment;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.object.*; import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.object.annotations.Snippet; import com.volmit.iris.engine.object.annotations.Snippet;
@@ -326,7 +325,7 @@ public class IrisProject {
} }
} }
for (Class<?> i : Iris.getClasses("com.volmit.iris.engine.object.", Snippet.class)) { for (Class<?> i : dm.resolveSnippets()) {
try { try {
String snipType = i.getDeclaredAnnotation(Snippet.class).value(); String snipType = i.getDeclaredAnnotation(Snippet.class).value();
JSONObject o = new JSONObject(); JSONObject o = new JSONObject();

View File

@@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull;
import java.awt.*; import java.awt.*;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -117,49 +118,13 @@ public class SchemaBuilder {
JSONArray required = new JSONArray(); JSONArray required = new JSONArray();
JSONArray extended = new JSONArray(); JSONArray extended = new JSONArray();
if (c.isAssignableFrom(IrisRegistrant.class) || IrisRegistrant.class.isAssignableFrom(c)) { var parent = c.getSuperclass();
for (Field k : IrisRegistrant.class.getDeclaredFields()) { while (parent != null && IrisRegistrant.class.isAssignableFrom(parent)) {
k.setAccessible(true); buildProperties(properties, required, extended, parent);
parent = parent.getSuperclass();
if (Modifier.isStatic(k.getModifiers()) || Modifier.isFinal(k.getModifiers()) || Modifier.isTransient(k.getModifiers())) {
continue;
} }
JSONObject property = buildProperty(k, c); buildProperties(properties, required, extended, c);
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
}
for (Field k : c.getDeclaredFields()) {
k.setAccessible(true);
if (Modifier.isStatic(k.getModifiers()) || Modifier.isFinal(k.getModifiers()) || Modifier.isTransient(k.getModifiers())) {
continue;
}
JSONObject property = buildProperty(k, c);
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
if (required.length() > 0) { if (required.length() > 0) {
o.put("required", required); o.put("required", required);
@@ -174,6 +139,33 @@ public class SchemaBuilder {
return buildSnippet(o, c); return buildSnippet(o, c);
} }
private void buildProperties(JSONObject properties, JSONArray required, JSONArray extended, Class<?> c) {
for (Field k : c.getDeclaredFields()) {
if (Modifier.isStatic(k.getModifiers()) || Modifier.isFinal(k.getModifiers()) || Modifier.isTransient(k.getModifiers())) {
continue;
}
try {
k.setAccessible(true);
} catch (InaccessibleObjectException e) {
continue;
}
JSONObject property = buildProperty(k, c);
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
properties.put(k.getName(), property);
}
}
private JSONObject buildProperty(Field k, Class<?> cl) { private JSONObject buildProperty(Field k, Class<?> cl) {
JSONObject prop = new JSONObject(); JSONObject prop = new JSONObject();
String type = getType(k.getType()); String type = getType(k.getType());
@@ -616,7 +608,7 @@ public class SchemaBuilder {
if (present) d.add(" "); if (present) d.add(" ");
if (value instanceof List) { if (value instanceof List) {
d.add(SYMBOL_LIMIT__N + " Default Value is an empty list"); d.add(SYMBOL_LIMIT__N + " Default Value is an empty list");
} else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !KeyedType.isKeyed(cl)) { } else if (!k.getType().isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(value instanceof Enum<?>) && !KeyedType.isKeyed(k.getType())) {
d.add(SYMBOL_LIMIT__N + " Default Value is a default object (create this object to see default properties)"); d.add(SYMBOL_LIMIT__N + " Default Value is a default object (create this object to see default properties)");
} else { } else {
d.add(SYMBOL_LIMIT__N + " Default Value is " + value); d.add(SYMBOL_LIMIT__N + " Default Value is " + value);

View File

@@ -2,6 +2,7 @@ package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment; import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import lombok.NonNull; import lombok.NonNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -16,4 +17,6 @@ public interface PackEnvironment extends SimpleEnvironment {
@Nullable @Nullable
Object createNoise(@NonNull String script, @NonNull RNG rng); Object createNoise(@NonNull String script, @NonNull RNG rng);
EngineEnvironment with(@NonNull Engine engine);
} }

View File

@@ -27,8 +27,4 @@ public interface SimpleEnvironment {
@Nullable @Nullable
Object evaluate(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars); Object evaluate(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
default void close() {
}
} }

View File

@@ -1,13 +1,11 @@
package com.volmit.iris.core.service; package com.volmit.iris.core.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.cache.PregenCache; import com.volmit.iris.core.pregenerator.cache.PregenCache;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.scheduling.Looper;
import lombok.NonNull; import lombok.NonNull;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
@@ -19,21 +17,33 @@ import org.bukkit.event.world.WorldUnloadEvent;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.function.Function; import java.util.function.Function;
public class GlobalCacheSVC implements IrisService { public class GlobalCacheSVC implements IrisService {
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder() private static final KMap<String, Reference<PregenCache>> REFERENCE_CACHE = new KMap<>();
.executor(KCache.EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.weakValues()
.build();
private final KMap<String, PregenCache> globalCache = new KMap<>(); private final KMap<String, PregenCache> globalCache = new KMap<>();
private transient boolean lastState; private transient boolean lastState;
private static boolean disabled = true; private static boolean disabled = true;
private Looper trimmer;
@Override @Override
public void onEnable() { public void onEnable() {
disabled = false; disabled = false;
trimmer = new Looper() {
@Override
protected long loop() {
var it = REFERENCE_CACHE.values().iterator();
while (it.hasNext()) {
var cache = it.next().get();
if (cache == null) it.remove();
else cache.trim(10_000);
}
return disabled ? -1 : 2_000;
}
};
trimmer.start();
lastState = !IrisSettings.get().getWorld().isGlobalPregenCache(); lastState = !IrisSettings.get().getWorld().isGlobalPregenCache();
if (lastState) return; if (lastState) return;
Bukkit.getWorlds().forEach(this::createCache); Bukkit.getWorlds().forEach(this::createCache);
@@ -42,6 +52,9 @@ public class GlobalCacheSVC implements IrisService {
@Override @Override
public void onDisable() { public void onDisable() {
disabled = true; disabled = true;
try {
trimmer.join();
} catch (InterruptedException ignored) {}
globalCache.qclear((world, cache) -> cache.write()); globalCache.qclear((world, cache) -> cache.write());
} }
@@ -76,6 +89,7 @@ public class GlobalCacheSVC implements IrisService {
} }
private void createCache(World world) { private void createCache(World world) {
if (!IrisToolbelt.isIrisWorld(world)) return;
globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault); globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault);
} }
@@ -99,7 +113,15 @@ public class GlobalCacheSVC implements IrisService {
@NonNull @NonNull
public static PregenCache createCache(@NonNull String worldName, @NonNull Function<String, PregenCache> provider) { public static PregenCache createCache(@NonNull String worldName, @NonNull Function<String, PregenCache> provider) {
return REFERENCE_CACHE.get(worldName, provider); PregenCache[] holder = new PregenCache[1];
REFERENCE_CACHE.compute(worldName, (name, ref) -> {
if (ref != null) {
if ((holder[0] = ref.get()) != null)
return ref;
}
return new WeakReference<>(holder[0] = provider.apply(worldName));
});
return holder[0];
} }
@NonNull @NonNull

View File

@@ -3,7 +3,7 @@ package com.volmit.iris.core.service;
import com.google.common.util.concurrent.AtomicDouble; import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.engine.platform.PlatformChunkGenerator;
@@ -14,6 +14,8 @@ import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.Looper;
import com.volmit.iris.util.stream.utility.CachedStream2D;
import com.volmit.iris.util.stream.utility.CachedStream3D;
import lombok.Synchronized; import lombok.Synchronized;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
@@ -27,6 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
public class IrisEngineSVC implements IrisService { public class IrisEngineSVC implements IrisService {
private static final int TRIM_PERIOD = 2_000;
private final AtomicInteger tectonicLimit = new AtomicInteger(30); private final AtomicInteger tectonicLimit = new AtomicInteger(30);
private final AtomicInteger tectonicPlates = new AtomicInteger(); private final AtomicInteger tectonicPlates = new AtomicInteger();
private final AtomicInteger queuedTectonicPlates = new AtomicInteger(); private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
@@ -64,10 +67,26 @@ public class IrisEngineSVC implements IrisService {
} }
public void engineStatus(VolmitSender sender) { public void engineStatus(VolmitSender sender) {
long[] sizes = new long[4];
long[] count = new long[4];
for (var cache : Iris.service(PreservationSVC.class).getCaches()) {
var type = switch (cache) {
case ResourceLoader<?> ignored -> 0;
case CachedStream2D<?> ignored -> 1;
case CachedStream3D<?> ignored -> 2;
default -> 3;
};
sizes[type] += cache.getSize();
count[type]++;
}
sender.sendMessage(C.DARK_PURPLE + "-------------------------"); sender.sendMessage(C.DARK_PURPLE + "-------------------------");
sender.sendMessage(C.DARK_PURPLE + "Status:"); sender.sendMessage(C.DARK_PURPLE + "Status:");
sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (service.isShutdown() ? "Shutdown" : "Running")); sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (service.isShutdown() ? "Shutdown" : "Running"));
sender.sendMessage(C.DARK_PURPLE + "- Updater: " + C.LIGHT_PURPLE + (updateTicker.isAlive() ? "Running" : "Stopped")); sender.sendMessage(C.DARK_PURPLE + "- Updater: " + C.LIGHT_PURPLE + (updateTicker.isAlive() ? "Running" : "Stopped"));
sender.sendMessage(C.DARK_PURPLE + "- Period: " + C.LIGHT_PURPLE + Form.duration(TRIM_PERIOD));
sender.sendMessage(C.DARK_PURPLE + "- Trimmers: " + C.LIGHT_PURPLE + trimmerAlive.get()); sender.sendMessage(C.DARK_PURPLE + "- Trimmers: " + C.LIGHT_PURPLE + trimmerAlive.get());
sender.sendMessage(C.DARK_PURPLE + "- Unloaders: " + C.LIGHT_PURPLE + unloaderAlive.get()); sender.sendMessage(C.DARK_PURPLE + "- Unloaders: " + C.LIGHT_PURPLE + unloaderAlive.get());
sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:"); sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:");
@@ -76,10 +95,14 @@ public class IrisEngineSVC implements IrisService {
sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + queuedTectonicPlates.get()); sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + queuedTectonicPlates.get());
sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2)); sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2));
sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2)); sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2));
sender.sendMessage(C.DARK_PURPLE + "Caches:");
sender.sendMessage(C.DARK_PURPLE + "- Resource: " + C.LIGHT_PURPLE + sizes[0] + " (" + count[0] + ")");
sender.sendMessage(C.DARK_PURPLE + "- 2D Stream: " + C.LIGHT_PURPLE + sizes[1] + " (" + count[1] + ")");
sender.sendMessage(C.DARK_PURPLE + "- 3D Stream: " + C.LIGHT_PURPLE + sizes[2] + " (" + count[2] + ")");
sender.sendMessage(C.DARK_PURPLE + "- Other: " + C.LIGHT_PURPLE + sizes[3] + " (" + count[3] + ")");
sender.sendMessage(C.DARK_PURPLE + "Other:"); sender.sendMessage(C.DARK_PURPLE + "Other:");
sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + totalWorlds.get()); sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + totalWorlds.get());
sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get()); sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get());
sender.sendMessage(C.DARK_PURPLE + "- Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
sender.sendMessage(C.DARK_PURPLE + "-------------------------"); sender.sendMessage(C.DARK_PURPLE + "-------------------------");
} }
@@ -113,12 +136,12 @@ public class IrisEngineSVC implements IrisService {
@Override @Override
protected long loop() { protected long loop() {
try { try {
queuedTectonicPlates.set(0); int queuedPlates = 0;
tectonicPlates.set(0); int totalPlates = 0;
loadedChunks.set(0); long chunks = 0;
unloaderAlive.set(0); int unloaders = 0;
trimmerAlive.set(0); int trimmers = 0;
totalWorlds.set(0); int iris = 0;
double maxDuration = Long.MIN_VALUE; double maxDuration = Long.MIN_VALUE;
double minDuration = Long.MAX_VALUE; double minDuration = Long.MAX_VALUE;
@@ -126,23 +149,30 @@ public class IrisEngineSVC implements IrisService {
var registered = entry.getValue(); var registered = entry.getValue();
if (registered.closed) continue; if (registered.closed) continue;
totalWorlds.incrementAndGet(); iris++;
unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0); if (registered.unloaderAlive()) unloaders++;
trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0); if (registered.trimmerAlive()) trimmers++;
var engine = registered.getEngine(); var engine = registered.getEngine();
if (engine == null) continue; if (engine == null) continue;
queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount()); queuedPlates += engine.getMantle().getUnloadRegionCount();
tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount()); totalPlates += engine.getMantle().getLoadedRegionCount();
loadedChunks.addAndGet(entry.getKey().getLoadedChunks().length); chunks += entry.getKey().getLoadedChunks().length;
double duration = engine.getMantle().getAdjustedIdleDuration(); double duration = engine.getMantle().getAdjustedIdleDuration();
if (duration > maxDuration) maxDuration = duration; if (duration > maxDuration) maxDuration = duration;
if (duration < minDuration) minDuration = duration; if (duration < minDuration) minDuration = duration;
} }
trimmerAlive.set(trimmers);
unloaderAlive.set(unloaders);
tectonicPlates.set(totalPlates);
queuedTectonicPlates.set(queuedPlates);
maxIdleDuration.set(maxDuration); maxIdleDuration.set(maxDuration);
minIdleDuration.set(minDuration); minIdleDuration.set(minDuration);
loadedChunks.set(chunks);
totalWorlds.set(iris);
worlds.values().forEach(Registered::update); worlds.values().forEach(Registered::update);
} catch (Throwable e) { } catch (Throwable e) {
@@ -157,7 +187,7 @@ public class IrisEngineSVC implements IrisService {
private final class Registered { private final class Registered {
private final String name; private final String name;
private final PlatformChunkGenerator access; private final PlatformChunkGenerator access;
private final int offset = RNG.r.nextInt(1000); private final int offset = RNG.r.nextInt(TRIM_PERIOD);
private transient ScheduledFuture<?> trimmer; private transient ScheduledFuture<?> trimmer;
private transient ScheduledFuture<?> unloader; private transient ScheduledFuture<?> unloader;
private transient boolean closed; private transient boolean closed;
@@ -194,7 +224,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to trim for " + name); Iris.error("EngineSVC: Failed to trim for " + name);
e.printStackTrace(); e.printStackTrace();
} }
}, offset, 2000, TimeUnit.MILLISECONDS); }, offset, TRIM_PERIOD, TimeUnit.MILLISECONDS);
} }
if (unloader == null || unloader.isDone() || unloader.isCancelled()) { if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
@@ -214,7 +244,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to unload for " + name); Iris.error("EngineSVC: Failed to unload for " + name);
e.printStackTrace(); e.printStackTrace();
} }
}, offset + 1000, 2000, TimeUnit.MILLISECONDS); }, offset + TRIM_PERIOD / 2, TRIM_PERIOD, TimeUnit.MILLISECONDS);
} }
} }

View File

@@ -24,19 +24,22 @@ import com.volmit.iris.engine.framework.MeteredCache;
import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.KCache; import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.Looper;
import org.jetbrains.annotations.Unmodifiable;
import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PreservationSVC implements IrisService { public class PreservationSVC implements IrisService {
private final List<Thread> threads = new CopyOnWriteArrayList<>(); private final List<Thread> threads = new CopyOnWriteArrayList<>();
private final List<ExecutorService> services = new CopyOnWriteArrayList<>(); private final List<ExecutorService> services = new CopyOnWriteArrayList<>();
private final List<MeteredCache> caches = new CopyOnWriteArrayList<>(); private final List<WeakReference<MeteredCache>> caches = new CopyOnWriteArrayList<>();
private Looper dereferencer; private Looper dereferencer;
public void register(Thread t) { public void register(Thread t) {
@@ -48,22 +51,18 @@ public class PreservationSVC implements IrisService {
} }
public void printCaches() { public void printCaches() {
long s = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getSize).sum(); var c = getCaches();
long m = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getMaxSize).sum(); long s = 0;
long m = 0;
double p = 0; double p = 0;
double mf = 0; double mf = Math.max(c.size(), 1);
for (MeteredCache i : caches) { for (MeteredCache i : c) {
if (i.isClosed()) { s += i.getSize();
continue; m += i.getMaxSize();
}
mf++;
p += i.getUsage(); p += i.getUsage();
} }
mf = mf == 0 ? 1 : mf;
Iris.info("Cached " + Form.f(s) + " / " + Form.f(m) + " (" + Form.pc(p / mf) + ") from " + caches.size() + " Caches"); Iris.info("Cached " + Form.f(s) + " / " + Form.f(m) + " (" + Form.pc(p / mf) + ") from " + caches.size() + " Caches");
} }
@@ -119,14 +118,29 @@ public class PreservationSVC implements IrisService {
} }
public void updateCaches() { public void updateCaches() {
caches.removeIf(MeteredCache::isClosed); caches.removeIf(ref -> {
var c = ref.get();
return c == null || c.isClosed();
});
} }
public void registerCache(MeteredCache cache) { public void registerCache(MeteredCache cache) {
caches.add(cache); caches.add(new WeakReference<>(cache));
} }
public List<KCache<?, ?>> caches() { public List<KCache<?, ?>> caches() {
return caches.stream().map(MeteredCache::getRawCache).collect(Collectors.toList()); return cacheStream().map(MeteredCache::getRawCache).collect(Collectors.toList());
}
@Unmodifiable
public List<MeteredCache> getCaches() {
return cacheStream().toList();
}
private Stream<MeteredCache> cacheStream() {
return caches.stream()
.map(WeakReference::get)
.filter(Objects::nonNull)
.filter(cache -> !cache.isClosed());
} }
} }

View File

@@ -189,6 +189,16 @@ public class TreeSVC implements IrisService {
} }
@Override
public <T> void setData(int xx, int yy, int zz, T data) {
}
@Override
public <T> T getData(int xx, int yy, int zz, Class<T> t) {
return null;
}
@Override @Override
public Engine getEngine() { public Engine getEngine() {
return engine; return engine;
@@ -225,7 +235,7 @@ public class TreeSVC implements IrisService {
if (d instanceof IrisCustomData data) { if (d instanceof IrisCustomData data) {
block.setBlockData(data.getBase(), false); block.setBlockData(data.getBase(), false);
Iris.service(ExternalDataSVC.class).processUpdate(engine, block, data.getCustom()); Iris.service(ExternalDataSVC.class).processUpdate(engine, block, data.getCustom());
} else block.setBlockData(d); } else block.setBlockData(d, false);
} }
} }
}); });

View File

@@ -90,6 +90,7 @@ public class WandSVC implements IrisService {
int total = c.getSizeX() * c.getSizeY() * c.getSizeZ(); int total = c.getSizeX() * c.getSizeY() * c.getSizeZ();
var latch = new CountDownLatch(1); var latch = new CountDownLatch(1);
var holder = Iris.tickets.getHolder(p.getWorld());
new Job() { new Job() {
private int i; private int i;
private Chunk chunk; private Chunk chunk;
@@ -108,7 +109,7 @@ public class WandSVC implements IrisService {
while (time > M.ms()) { while (time > M.ms()) {
if (!it.hasNext()) { if (!it.hasNext()) {
if (chunk != null) { if (chunk != null) {
chunk.removePluginChunkTicket(Iris.instance); holder.removeTicket(chunk);
chunk = null; chunk = null;
} }
@@ -122,9 +123,10 @@ public class WandSVC implements IrisService {
var bChunk = b.getChunk(); var bChunk = b.getChunk();
if (chunk == null) { if (chunk == null) {
chunk = bChunk; chunk = bChunk;
chunk.addPluginChunkTicket(Iris.instance); holder.addTicket(chunk);
} else if (chunk != bChunk) { } else if (chunk != bChunk) {
chunk.removePluginChunkTicket(Iris.instance); holder.removeTicket(chunk);
holder.addTicket(bChunk);
chunk = bChunk; chunk = bChunk;
} }

View File

@@ -34,6 +34,7 @@ import com.volmit.iris.util.plugin.VolmitSender;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -45,6 +46,7 @@ import java.util.Map;
* Hope you packed snacks & road sodas. * Hope you packed snacks & road sodas.
*/ */
public class IrisToolbelt { public class IrisToolbelt {
@ApiStatus.Internal
public static Map<String, Boolean> toolbeltConfiguration = new HashMap<>(); public static Map<String, Boolean> toolbeltConfiguration = new HashMap<>();
/** /**
@@ -232,7 +234,11 @@ public class IrisToolbelt {
} }
public static void retainMantleDataForSlice(String className) { public static void retainMantleDataForSlice(String className) {
toolbeltConfiguration.put("retain.mantle." + className, true); toolbeltConfiguration.put("retain.mantle." + className, Boolean.TRUE);
}
public static boolean isRetainingMantleDataForSlice(String className) {
return !toolbeltConfiguration.isEmpty() && toolbeltConfiguration.get("retain.mantle." + className) == Boolean.TRUE;
} }
public static <T> T getMantleData(World world, int x, int y, int z, Class<T> of) { public static <T> T getMantleData(World world, int x, int y, int z, Class<T> of) {

View File

@@ -89,7 +89,7 @@ public class IrisComplex implements DataProvider {
} }
public IrisComplex(Engine engine, boolean simple) { public IrisComplex(Engine engine, boolean simple) {
int cacheSize = IrisSettings.get().getPerformance().getCacheSize(); int cacheSize = IrisSettings.get().getPerformance().getNoiseCacheSize();
IrisBiome emptyBiome = new IrisBiome(); IrisBiome emptyBiome = new IrisBiome();
UUID focusUUID = UUID.nameUUIDFromBytes("focus".getBytes()); UUID focusUUID = UUID.nameUUIDFromBytes("focus".getBytes());
this.rng = new RNG(engine.getSeedManager().getComplex()); this.rng = new RNG(engine.getSeedManager().getComplex());

View File

@@ -125,6 +125,12 @@ public class IrisEngine implements Engine {
mantle = new IrisEngineMantle(this); mantle = new IrisEngineMantle(this);
context = new IrisContext(this); context = new IrisContext(this);
cleaning = new AtomicBoolean(false); cleaning = new AtomicBoolean(false);
execution = getData().getEnvironment().with(this);
if (studio) {
getData().dump();
getData().clearLists();
getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey()));
}
context.touch(); context.touch();
getData().setEngine(this); getData().setEngine(this);
getData().loadPrefetch(this); getData().loadPrefetch(this);
@@ -157,9 +163,9 @@ public class IrisEngine implements Engine {
private void prehotload() { private void prehotload() {
worldManager.close(); worldManager.close();
complex.close(); complex.close();
execution.close();
effects.close(); effects.close();
mode.close(); mode.close();
execution = getData().getEnvironment().with(this);
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace()); J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
} }
@@ -170,7 +176,6 @@ public class IrisEngine implements Engine {
cacheId = RNG.r.nextInt(); cacheId = RNG.r.nextInt();
worldManager = new IrisWorldManager(this); worldManager = new IrisWorldManager(this);
complex = new IrisComplex(this); complex = new IrisComplex(this);
execution = EngineEnvironment.create(this);
effects = new IrisEngineEffects(this); effects = new IrisEngineEffects(this);
hash32 = new CompletableFuture<>(); hash32 = new CompletableFuture<>();
mantle.hotload(); mantle.hotload();

View File

@@ -86,11 +86,13 @@ public class IrisEngineMantle implements EngineMantle {
.map(components::get) .map(components::get)
.map(components -> { .map(components -> {
int radius = components.stream() int radius = components.stream()
.filter(MantleComponent::isEnabled)
.mapToInt(MantleComponent::getRadius) .mapToInt(MantleComponent::getRadius)
.max() .max()
.orElse(0); .orElse(0);
return new Pair<>(List.copyOf(components), radius); return new Pair<>(List.copyOf(components), radius);
}) })
.filter(pair -> !pair.getA().isEmpty())
.toList(); .toList();
int radius = 0; int radius = 0;

View File

@@ -440,7 +440,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
//INMS.get().injectBiomesFromMantle(e, getMantle()); //INMS.get().injectBiomesFromMantle(e, getMantle());
if (!IrisSettings.get().getGenerator().earlyCustomBlocks) return; if (!IrisSettings.get().getGenerator().earlyCustomBlocks) return;
e.addPluginChunkTicket(Iris.instance); Iris.tickets.addTicket(e);
J.s(() -> { J.s(() -> {
var chunk = getMantle().getChunk(e).use(); var chunk = getMantle().getChunk(e).use();
int minY = getTarget().getWorld().minHeight(); int minY = getTarget().getWorld().minHeight();
@@ -452,7 +452,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}); });
} finally { } finally {
chunk.release(); chunk.release();
e.removePluginChunkTicket(Iris.instance); Iris.tickets.removeTicket(e);
} }
}, RNG.r.i(20, 60)); }, RNG.r.i(20, 60));
} }

View File

@@ -18,6 +18,7 @@
package com.volmit.iris.engine.framework; package com.volmit.iris.engine.framework;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@@ -28,5 +29,5 @@ public interface BlockUpdater {
void updateChunk(Chunk c); void updateChunk(Chunk c);
void update(int x, int y, int z, Chunk c, RNG rf); void update(int x, int y, int z, Chunk c, MantleChunk mc, RNG rf);
} }

View File

@@ -48,6 +48,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.function.Function2; import com.volmit.iris.util.function.Function2;
import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.BlockPosition; import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
@@ -295,7 +296,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
var chunk = mantle.getChunk(c).use(); var chunk = mantle.getChunk(c).use();
try { try {
Semaphore semaphore = new Semaphore(1024); Semaphore semaphore = new Semaphore(1024);
chunk.raiseFlag(MantleFlag.ETCHED, () -> { chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> { chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> {
chunk.iterate(TileWrapper.class, (x, y, z, v) -> { chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15); Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
@@ -340,14 +341,14 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
if (grid[x][z] == Integer.MIN_VALUE) if (grid[x][z] == Integer.MIN_VALUE)
continue; continue;
update(x, grid[x][z], z, c, rng); update(x, grid[x][z], z, c, chunk, rng);
} }
} }
chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> { chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight(); int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) { if (v != null && v.isUpdate()) {
update(x, y, z, c, rng); update(x, y, z, c, chunk, rng);
} }
}); });
chunk.deleteSlices(MatterUpdate.class); chunk.deleteSlices(MatterUpdate.class);
@@ -394,7 +395,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@BlockCoordinates @BlockCoordinates
@Override @Override
default void update(int x, int y, int z, Chunk c, RNG rf) { default void update(int x, int y, int z, Chunk c, MantleChunk mc, RNG rf) {
Block block = c.getBlock(x, y, z); Block block = c.getBlock(x, y, z);
BlockData data = block.getBlockData(); BlockData data = block.getBlockData();
blockUpdatedMetric(); blockUpdatedMetric();
@@ -407,17 +408,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
} }
if (slot != null) { if (slot != null) {
KList<IrisLootTable> tables = getLootTables(rx, block); KList<IrisLootTable> tables = getLootTables(rx, block, mc);
try { try {
Bukkit.getPluginManager().callEvent(new IrisLootEvent(this, block, slot, tables)); Bukkit.getPluginManager().callEvent(new IrisLootEvent(this, block, slot, tables));
if (tables.isEmpty()) return;
if (!tables.isEmpty()){
Iris.debug("IrisLootEvent has been accessed");
}
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState(); InventoryHolder m = (InventoryHolder) block.getState();
addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15); addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15);
@@ -483,13 +478,23 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@BlockCoordinates @BlockCoordinates
@Override @Override
default KList<IrisLootTable> getLootTables(RNG rng, Block b) { default KList<IrisLootTable> getLootTables(RNG rng, Block b) {
MantleChunk mc = getMantle().getMantle().getChunk(b.getChunk()).use();
try {
return getLootTables(rng, b, mc);
} finally {
mc.release();
}
}
@BlockCoordinates
default KList<IrisLootTable> getLootTables(RNG rng, Block b, MantleChunk mc) {
int rx = b.getX(); int rx = b.getX();
int rz = b.getZ(); int rz = b.getZ();
int ry = b.getY() - getWorld().minHeight(); int ry = b.getY() - getWorld().minHeight();
double he = getComplex().getHeightStream().get(rx, rz); double he = getComplex().getHeightStream().get(rx, rz);
KList<IrisLootTable> tables = new KList<>(); KList<IrisLootTable> tables = new KList<>();
PlacedObject po = getObjectPlacement(rx, ry, rz); PlacedObject po = getObjectPlacement(rx, ry, rz, mc);
if (po != null && po.getPlacement() != null) { if (po != null && po.getPlacement() != null) {
if (B.isStorageChest(b.getBlockData())) { if (B.isStorageChest(b.getBlockData())) {
IrisLootTable table = po.getPlacement().getTable(b.getBlockData(), getData()); IrisLootTable table = po.getPlacement().getTable(b.getBlockData(), getData());
@@ -812,7 +817,16 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
} }
default PlacedObject getObjectPlacement(int x, int y, int z) { default PlacedObject getObjectPlacement(int x, int y, int z) {
String objectAt = getMantle().getMantle().get(x, y, z, String.class); MantleChunk chunk = getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
try {
return getObjectPlacement(x, y, z, chunk);
} finally {
chunk.release();
}
}
default PlacedObject getObjectPlacement(int x, int y, int z, MantleChunk chunk) {
String objectAt = chunk.get(x & 15, y, z & 15, String.class);
if (objectAt == null || objectAt.isEmpty()) { if (objectAt == null || objectAt.isEmpty()) {
return null; return null;
} }
@@ -822,7 +836,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
int id = Integer.parseInt(v[1]); int id = Integer.parseInt(v[1]);
JigsawPieceContainer container = getMantle().getMantle().get(x, y, z, JigsawPieceContainer.class); JigsawPieceContainer container = chunk.get(x & 15, y, z & 15, JigsawPieceContainer.class);
if (container != null) { if (container != null) {
IrisJigsawPiece piece = container.load(getData()); IrisJigsawPiece piece = container.load(getData());
if (piece.getObject().equals(object)) if (piece.getObject().equals(object))

View File

@@ -24,7 +24,6 @@ import com.volmit.iris.engine.object.IObjectPlacer;
import com.volmit.iris.engine.object.IrisObjectPlacement; import com.volmit.iris.engine.object.IrisObjectPlacement;
import com.volmit.iris.engine.object.TileData; import com.volmit.iris.engine.object.TileData;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
public class HeightmapObjectPlacer implements IObjectPlacer { public class HeightmapObjectPlacer implements IObjectPlacer {
@@ -83,6 +82,16 @@ public class HeightmapObjectPlacer implements IObjectPlacer {
oplacer.setTile(param1Int1, param1Int2, param1Int3, param1TileData); oplacer.setTile(param1Int1, param1Int2, param1Int3, param1TileData);
} }
@Override
public <T> void setData(int xx, int yy, int zz, T data) {
oplacer.setData(xx, yy, zz, data);
}
@Override
public <T> T getData(int xx, int yy, int zz, Class<T> t) {
return oplacer.getData(xx, yy, zz, t);
}
@Override @Override
public Engine getEngine() { public Engine getEngine() {
return null; return null;

View File

@@ -59,6 +59,11 @@ public class WorldObjectPlacer implements IObjectPlacer {
slot = InventorySlotType.STORAGE; slot = InventorySlotType.STORAGE;
} }
if (d instanceof IrisCustomData data) {
block.setBlockData(data.getBase(), false);
Iris.warn("Tried to place custom block at " + x + ", " + y + ", " + z + " which is not supported!");
} else block.setBlockData(d, false);
if (slot != null) { if (slot != null) {
RNG rx = new RNG(Cache.key(x, z)); RNG rx = new RNG(Cache.key(x, z));
KList<IrisLootTable> tables = engine.getLootTables(rx, block); KList<IrisLootTable> tables = engine.getLootTables(rx, block);
@@ -78,12 +83,6 @@ public class WorldObjectPlacer implements IObjectPlacer {
Iris.reportError(e); Iris.reportError(e);
} }
} }
if (d instanceof IrisCustomData data) {
block.setBlockData(data.getBase());
Iris.warn("Tried to place custom block at " + x + ", " + y + ", " + z + " which is not supported!");
} else block.setBlockData(d);
} }
@Override @Override
@@ -125,4 +124,13 @@ public class WorldObjectPlacer implements IObjectPlacer {
public void setTile(int xx, int yy, int zz, TileData tile) { public void setTile(int xx, int yy, int zz, TileData tile) {
tile.toBukkitTry(world.getBlockAt(xx, yy + world.getMinHeight(), zz)); tile.toBukkitTry(world.getBlockAt(xx, yy + world.getMinHeight(), zz));
} }
@Override
public <T> void setData(int xx, int yy, int zz, T data) {
}
@Override
public <T> T getData(int xx, int yy, int zz, Class<T> t) {
return null;
}
} }

View File

@@ -154,9 +154,9 @@ public class PlannedStructure {
JigsawStructureContainer structure = JigsawStructureContainer.toContainer(getStructure()); JigsawStructureContainer structure = JigsawStructureContainer.toContainer(getStructure());
i.setRealPositions(xx, height, zz, placer); i.setRealPositions(xx, height, zz, placer);
return v.place(xx, height, zz, placer, options, rng, (b, data) -> { return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id); placer.setData(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
e.set(b.getX(), b.getY(), b.getZ(), structure); placer.setData(b.getX(), b.getY(), b.getZ(), structure);
e.set(b.getX(), b.getY(), b.getZ(), piece); placer.setData(b.getX(), b.getY(), b.getZ(), piece);
}, null, getData().getEngine() != null ? getData() : eng.getData()) != -1; }, null, getData().getEngine() != null ? getData() : eng.getData()) != -1;
} }

View File

@@ -29,8 +29,6 @@ import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisPosition; import com.volmit.iris.engine.object.IrisPosition;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.B;
import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates;
@@ -38,7 +36,6 @@ import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.matter.*; import com.volmit.iris.util.matter.*;
import com.volmit.iris.util.matter.slices.UpdateMatter; import com.volmit.iris.util.matter.slices.UpdateMatter;
import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.parallel.MultiBurst;
@@ -49,10 +46,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.volmit.iris.util.parallel.StreamUtils.forEach; public interface EngineMantle extends MatterGenerator {
import static com.volmit.iris.util.parallel.StreamUtils.streamRadius;
public interface EngineMantle {
BlockData AIR = B.get("AIR"); BlockData AIR = B.get("AIR");
Mantle getMantle(); Mantle getMantle();
@@ -180,49 +174,6 @@ public interface EngineMantle {
return getEngine().burst(); return getEngine().burst();
} }
@ChunkCoordinates
default void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
if (!getEngine().getDimension().isUseMantle() || getMantle().hasFlag(x, z, MantleFlag.PLANNED)) {
return;
}
try (MantleWriter writer = getMantle().write(this, x, z, getRadius() * 2)) {
var iterator = getComponents().iterator();
while (iterator.hasNext()) {
var pair = iterator.next();
int radius = pair.getB();
boolean last = !iterator.hasNext();
forEach(streamRadius(x, z, radius),
pos -> pair.getA()
.stream()
.filter(MantleComponent::isEnabled)
.map(c -> new Pair<>(c, pos)),
p -> {
MantleComponent c = p.getA();
Position2 pos = p.getB();
int xx = pos.getX();
int zz = pos.getZ();
IrisContext.getOr(getEngine()).setChunkContext(context);
generateMantleComponent(writer, xx, zz, c, writer.acquireChunk(xx, zz), context);
},
multicore ? burst() : null
);
if (!last) continue;
forEach(streamRadius(x, z, radius),
p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true),
multicore ? burst() : null
);
}
}
}
default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) {
mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> {
if (c.isEnabled()) c.generateLayer(writer, x, z, context);
});
}
@ChunkCoordinates @ChunkCoordinates
default <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> blocks, boolean multicore) { default <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> blocks, boolean multicore) {
if (!getEngine().getDimension().isUseMantle()) { if (!getEngine().getDimension().isUseMantle()) {
@@ -262,9 +213,6 @@ public interface EngineMantle {
default int getLoadedRegionCount() { default int getLoadedRegionCount() {
return getMantle().getLoadedRegionCount(); return getMantle().getLoadedRegionCount();
} }
default long getLastUseMapMemoryUsage(){
return getMantle().LastUseMapMemoryUsage();
}
MantleJigsawComponent getJigsawComponent(); MantleJigsawComponent getJigsawComponent();
@@ -290,7 +238,7 @@ public interface EngineMantle {
if (!isCovered(x, z)) return; if (!isCovered(x, z)) return;
MantleChunk chunk = getMantle().getChunk(x, z).use(); MantleChunk chunk = getMantle().getChunk(x, z).use();
try { try {
chunk.raiseFlag(MantleFlag.CLEANED, () -> { chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> {
chunk.deleteSlices(BlockData.class); chunk.deleteSlices(BlockData.class);
chunk.deleteSlices(String.class); chunk.deleteSlices(String.class);
chunk.deleteSlices(MatterCavern.class); chunk.deleteSlices(MatterCavern.class);
@@ -301,7 +249,7 @@ public interface EngineMantle {
} }
} }
default long getUnloadRegionCount() { default int getUnloadRegionCount() {
return getMantle().getUnloadRegionCount(); return getMantle().getUnloadRegionCount();
} }

View File

@@ -44,9 +44,7 @@ import lombok.Data;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.util.HashSet; import java.util.*;
import java.util.List;
import java.util.Set;
import static com.volmit.iris.engine.mantle.EngineMantle.AIR; import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
@@ -54,20 +52,21 @@ import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
public class MantleWriter implements IObjectPlacer, AutoCloseable { public class MantleWriter implements IObjectPlacer, AutoCloseable {
private final EngineMantle engineMantle; private final EngineMantle engineMantle;
private final Mantle mantle; private final Mantle mantle;
private final KMap<Long, MantleChunk> cachedChunks; private final Map<Long, MantleChunk> cachedChunks;
private final int radius; private final int radius;
private final int x; private final int x;
private final int z; private final int z;
public MantleWriter(EngineMantle engineMantle, Mantle mantle, int x, int z, int radius) { public MantleWriter(EngineMantle engineMantle, Mantle mantle, int x, int z, int radius, boolean multicore) {
this.engineMantle = engineMantle; this.engineMantle = engineMantle;
this.mantle = mantle; this.mantle = mantle;
this.cachedChunks = new KMap<>(); this.radius = radius * 2;
this.radius = radius; int d = this.radius + 1;
this.cachedChunks = multicore ? new KMap<>(d * d, 0.75f, Math.max(32, Runtime.getRuntime().availableProcessors() * 4)) : new HashMap<>(d * d);
this.x = x; this.x = x;
this.z = z; this.z = z;
int r = radius / 4; int r = radius / 2;
for (int i = -r; i <= r; i++) { for (int i = -r; i <= r; i++) {
for (int j = -r; j <= r; j++) { for (int j = -r; j <= r; j++) {
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use()); cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
@@ -182,8 +181,13 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz); Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
return null; return null;
} }
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use()); final Long key = Cache.key(cx, cz);
if (chunk == null) Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); MantleChunk chunk = cachedChunks.get(key);
if (chunk == null) {
chunk = mantle.getChunk(cx, cz).use();
var old = cachedChunks.put(key, chunk);
if (old != null) old.release();
}
return chunk; return chunk;
} }

View File

@@ -35,7 +35,6 @@ import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.noise.CNG; import com.volmit.iris.util.noise.CNG;
import lombok.Getter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
@@ -45,7 +44,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng; private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) { public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, ReservedFlag.JIGSAW, 2); super(engineMantle, ReservedFlag.JIGSAW, 1);
cng = NoiseStyle.STATIC.create(new RNG(jigsaw())); cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
} }

View File

@@ -119,7 +119,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
if (y > k.getMaxHeight() || y < k.getMinHeight() || y > height - 2) if (y > k.getMaxHeight() || y < k.getMinHeight() || y > height - 2)
continue; continue;
for (BlockVector j : clump.getBlocks().keySet()) { for (BlockVector j : clump.getBlocks().keys()) {
int nx = j.getBlockX() + x; int nx = j.getBlockX() + x;
int ny = j.getBlockY() + y; int ny = j.getBlockY() + y;
int nz = j.getBlockZ() + z; int nz = j.getBlockZ() + z;

View File

@@ -20,8 +20,8 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.Nullable;
public interface IObjectPlacer { public interface IObjectPlacer {
int getHighest(int x, int z, IrisData data); int getHighest(int x, int z, IrisData data);
@@ -46,5 +46,9 @@ public interface IObjectPlacer {
void setTile(int xx, int yy, int zz, TileData tile); void setTile(int xx, int yy, int zz, TileData tile);
<T> void setData(int xx, int yy, int zz, T data);
<T> @Nullable T getData(int xx, int yy, int zz, Class<T> t);
Engine getEngine(); Engine getEngine();
} }

View File

@@ -122,6 +122,10 @@ public class IrisCompat {
private static KList<IrisCompatabilityBlockFilter> getDefaultBlockCompatabilityFilters() { private static KList<IrisCompatabilityBlockFilter> getDefaultBlockCompatabilityFilters() {
KList<IrisCompatabilityBlockFilter> filters = new KList<>(); KList<IrisCompatabilityBlockFilter> filters = new KList<>();
filters.add(new IrisCompatabilityBlockFilter("CHAIN", "IRON_CHAIN"));
filters.add(new IrisCompatabilityBlockFilter("GRASS", "SHORT_GRASS"));
filters.add(new IrisCompatabilityBlockFilter("SHORT_GRASS", "GRASS"));
// Below 1.16 // Below 1.16
filters.add(new IrisCompatabilityBlockFilter("WEEPING_VINES", "NETHER_FENCE")); filters.add(new IrisCompatabilityBlockFilter("WEEPING_VINES", "NETHER_FENCE"));
filters.add(new IrisCompatabilityBlockFilter("WEEPING_VINES_PLANT", "NETHER_FENCE")); filters.add(new IrisCompatabilityBlockFilter("WEEPING_VINES_PLANT", "NETHER_FENCE"));
@@ -160,7 +164,7 @@ public class IrisCompat {
filters.add(new IrisCompatabilityBlockFilter("CRACKED_NETHER_BRICKS", "NETHER_BRICKS")); filters.add(new IrisCompatabilityBlockFilter("CRACKED_NETHER_BRICKS", "NETHER_BRICKS"));
filters.add(new IrisCompatabilityBlockFilter("CHISELED_NETHER_BRICKS", "NETHER_BRICKS")); filters.add(new IrisCompatabilityBlockFilter("CHISELED_NETHER_BRICKS", "NETHER_BRICKS"));
filters.add(new IrisCompatabilityBlockFilter("NETHER_FENCE", "LEGACY_NETHER_FENCE")); filters.add(new IrisCompatabilityBlockFilter("NETHER_FENCE", "LEGACY_NETHER_FENCE"));
filters.add(new IrisCompatabilityBlockFilter("CHAIN", "IRON_BARS")); filters.add(new IrisCompatabilityBlockFilter("IRON_CHAIN", "IRON_BARS"));
filters.add(new IrisCompatabilityBlockFilter("NETHERITE_BLOCK", "QUARTZ_BLOCK")); filters.add(new IrisCompatabilityBlockFilter("NETHERITE_BLOCK", "QUARTZ_BLOCK"));
filters.add(new IrisCompatabilityBlockFilter("BLACKSTONE", "COBBLESTONE")); filters.add(new IrisCompatabilityBlockFilter("BLACKSTONE", "COBBLESTONE"));
filters.add(new IrisCompatabilityBlockFilter("BASALT", "STONE")); filters.add(new IrisCompatabilityBlockFilter("BASALT", "STONE"));
@@ -254,7 +258,6 @@ public class IrisCompat {
filters.add(new IrisCompatabilityBlockFilter("BAMBOO", "BIRCH_FENCE")); filters.add(new IrisCompatabilityBlockFilter("BAMBOO", "BIRCH_FENCE"));
filters.add(new IrisCompatabilityBlockFilter("BAMBOO_SAPLING", "BIRCH_SAPLING")); filters.add(new IrisCompatabilityBlockFilter("BAMBOO_SAPLING", "BIRCH_SAPLING"));
filters.add(new IrisCompatabilityBlockFilter("POTTED_BAMBOO", "POTTED_BIRCH_SAPLING")); filters.add(new IrisCompatabilityBlockFilter("POTTED_BAMBOO", "POTTED_BIRCH_SAPLING"));
filters.add(new IrisCompatabilityBlockFilter("GRASS", "SHORT_GRASS"));
return filters; return filters;
} }

View File

@@ -30,6 +30,7 @@ import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.data.IrisCustomData;
import com.volmit.iris.util.data.VectorMap;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.interpolation.IrisInterpolation; import com.volmit.iris.util.interpolation.IrisInterpolation;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
@@ -38,7 +39,6 @@ import com.volmit.iris.util.matter.MatterMarker;
import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender; 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.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.scheduling.jobs.Job; import com.volmit.iris.util.scheduling.jobs.Job;
import com.volmit.iris.util.stream.ProceduralStream; import com.volmit.iris.util.stream.ProceduralStream;
@@ -62,7 +62,10 @@ import java.util.*;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.stream.StreamSupport;
@Accessors(chain = true) @Accessors(chain = true)
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@@ -72,17 +75,17 @@ public class IrisObject extends IrisRegistrant {
protected static final BlockData VAIR = B.get("VOID_AIR"); protected static final BlockData VAIR = B.get("VOID_AIR");
protected static final BlockData VAIR_DEBUG = B.get("COBWEB"); 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 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"); protected transient final Lock readLock;
protected transient final Lock writeLock;
@Getter @Getter
@Setter @Setter
protected transient volatile boolean smartBored = false; protected transient volatile boolean smartBored = false;
@Getter
@Setter
protected transient IrisLock lock = new IrisLock("Preloadcache");
@Setter @Setter
protected transient AtomicCache<AxisAlignedBB> aabb = new AtomicCache<>(); protected transient AtomicCache<AxisAlignedBB> aabb = new AtomicCache<>();
private KMap<Vector3i, BlockData> blocks; @Getter
private KMap<Vector3i, TileData> states; private VectorMap<BlockData> blocks;
@Getter
private VectorMap<TileData> states;
@Getter @Getter
@Setter @Setter
private int w; private int w;
@@ -97,12 +100,15 @@ public class IrisObject extends IrisRegistrant {
private transient Vector3i center; private transient Vector3i center;
public IrisObject(int w, int h, int d) { public IrisObject(int w, int h, int d) {
blocks = new KMap<>(); blocks = new VectorMap<>();
states = new KMap<>(); states = new VectorMap<>();
this.w = w; this.w = w;
this.h = h; this.h = h;
this.d = d; this.d = d;
center = new Vector3i(w / 2, h / 2, d / 2); center = new Vector3i(w / 2, h / 2, d / 2);
var lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
} }
public IrisObject() { public IrisObject() {
@@ -158,10 +164,10 @@ public class IrisObject extends IrisRegistrant {
PrecisionStopwatch p = PrecisionStopwatch.start(); PrecisionStopwatch p = PrecisionStopwatch.start();
BlockData vair = debug ? VAIR_DEBUG : VAIR; BlockData vair = debug ? VAIR_DEBUG : VAIR;
lock.lock(); writeLock.lock();
AtomicInteger applied = new AtomicInteger(); AtomicInteger applied = new AtomicInteger();
if (getBlocks().isEmpty()) { if (blocks.isEmpty()) {
lock.unlock(); writeLock.unlock();
Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it."); Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it.");
smartBored = true; smartBored = true;
return; return;
@@ -170,7 +176,7 @@ public class IrisObject extends IrisRegistrant {
BlockVector max = new BlockVector(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE); 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); BlockVector min = new BlockVector(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
for (BlockVector i : getBlocks().keySet()) { for (BlockVector i : blocks.keys()) {
max.setX(Math.max(i.getX(), max.getX())); max.setX(Math.max(i.getX(), max.getX()));
min.setX(Math.min(i.getX(), min.getX())); min.setX(Math.min(i.getX(), min.getX()));
max.setY(Math.max(i.getY(), max.getY())); max.setY(Math.max(i.getY(), max.getY()));
@@ -190,7 +196,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE; int end = Integer.MIN_VALUE;
for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) { for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) {
if (getBlocks().containsKey(new Vector3i(ray, finalRayY, rayZ))) { if (blocks.containsKey(new Vector3i(ray, finalRayY, rayZ))) {
start = Math.min(ray, start); start = Math.min(ray, start);
end = Math.max(ray, end); end = Math.max(ray, end);
} }
@@ -200,8 +206,8 @@ public class IrisObject extends IrisRegistrant {
for (int i = start; i <= end; i++) { for (int i = start; i <= end; i++) {
Vector3i v = new Vector3i(i, finalRayY, rayZ); Vector3i v = new Vector3i(i, finalRayY, rayZ);
if (!B.isAir(getBlocks().get(v))) { if (!vair.equals(blocks.get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair); blocks.computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement(); applied.getAndIncrement();
} }
} }
@@ -219,7 +225,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE; int end = Integer.MIN_VALUE;
for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) { for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) {
if (getBlocks().containsKey(new Vector3i(finalRayX, ray, rayZ))) { if (blocks.containsKey(new Vector3i(finalRayX, ray, rayZ))) {
start = Math.min(ray, start); start = Math.min(ray, start);
end = Math.max(ray, end); end = Math.max(ray, end);
} }
@@ -229,8 +235,8 @@ public class IrisObject extends IrisRegistrant {
for (int i = start; i <= end; i++) { for (int i = start; i <= end; i++) {
Vector3i v = new Vector3i(finalRayX, i, rayZ); Vector3i v = new Vector3i(finalRayX, i, rayZ);
if (!B.isAir(getBlocks().get(v))) { if (!vair.equals(blocks.get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair); blocks.computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement(); applied.getAndIncrement();
} }
} }
@@ -248,7 +254,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE; int end = Integer.MIN_VALUE;
for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) { for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) {
if (getBlocks().containsKey(new Vector3i(finalRayX, rayY, ray))) { if (blocks.containsKey(new Vector3i(finalRayX, rayY, ray))) {
start = Math.min(ray, start); start = Math.min(ray, start);
end = Math.max(ray, end); end = Math.max(ray, end);
} }
@@ -258,8 +264,8 @@ public class IrisObject extends IrisRegistrant {
for (int i = start; i <= end; i++) { for (int i = start; i <= end; i++) {
Vector3i v = new Vector3i(finalRayX, rayY, i); Vector3i v = new Vector3i(finalRayX, rayY, i);
if (!B.isAir(getBlocks().get(v))) { if (!vair.equals(blocks.get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair); blocks.computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement(); applied.getAndIncrement();
} }
} }
@@ -270,7 +276,7 @@ public class IrisObject extends IrisRegistrant {
burst.complete(); burst.complete();
smartBored = true; smartBored = true;
lock.unlock(); writeLock.unlock();
Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")"); Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")");
} }
@@ -281,13 +287,8 @@ public class IrisObject extends IrisRegistrant {
o.setLoadFile(getLoadFile()); o.setLoadFile(getLoadFile());
o.setCenter(getCenter().clone()); o.setCenter(getCenter().clone());
for (Vector3i i : getBlocks().keySet()) { blocks.forEach((i, v) -> o.blocks.put(i.clone(), v.clone()));
o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone()); states.forEach((i, v) -> o.states.put(i.clone(), v.clone()));
}
for (Vector3i i : getStates().keySet()) {
o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone());
}
return o; return o;
} }
@@ -301,14 +302,14 @@ public class IrisObject extends IrisRegistrant {
int s = din.readInt(); int s = din.readInt();
for (int i = 0; i < s; i++) { for (int i = 0; i < s; i++) {
getBlocks().put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF())); blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF()));
} }
try { try {
int size = din.readInt(); int size = din.readInt();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
getStates().put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); states.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
} }
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
@@ -336,13 +337,13 @@ public class IrisObject extends IrisRegistrant {
s = din.readInt(); s = din.readInt();
for (i = 0; i < s; i++) { for (i = 0; i < s; i++) {
getBlocks().put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort()))); blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort())));
} }
s = din.readInt(); s = din.readInt();
for (i = 0; i < s; i++) { for (i = 0; i < s; i++) {
getStates().put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din)); states.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
} }
} }
@@ -354,7 +355,7 @@ public class IrisObject extends IrisRegistrant {
dos.writeUTF("Iris V2 IOB;"); dos.writeUTF("Iris V2 IOB;");
KList<String> palette = new KList<>(); KList<String> palette = new KList<>();
for (BlockData i : getBlocks().values()) { for (BlockData i : blocks.values()) {
palette.addIfMissing(i.getAsString()); palette.addIfMissing(i.getAsString());
} }
@@ -364,21 +365,23 @@ public class IrisObject extends IrisRegistrant {
dos.writeUTF(i); dos.writeUTF(i);
} }
dos.writeInt(getBlocks().size()); dos.writeInt(blocks.size());
for (Vector3i i : getBlocks().keySet()) { for (var entry : blocks) {
var i = entry.getKey();
dos.writeShort(i.getBlockX()); dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY()); dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ()); dos.writeShort(i.getBlockZ());
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString())); dos.writeShort(palette.indexOf(entry.getValue().getAsString()));
} }
dos.writeInt(getStates().size()); dos.writeInt(states.size());
for (Vector3i i : getStates().keySet()) { for (var entry : states) {
var i = entry.getKey();
dos.writeShort(i.getBlockX()); dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY()); dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ()); dos.writeShort(i.getBlockZ());
getStates().get(i).toBinary(dos); entry.getValue().toBinary(dos);
} }
} }
@@ -386,7 +389,7 @@ public class IrisObject extends IrisRegistrant {
AtomicReference<IOException> ref = new AtomicReference<>(); AtomicReference<IOException> ref = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
new Job() { new Job() {
private int total = getBlocks().size() * 3 + getStates().size(); private int total = blocks.size() * 3 + states.size();
private int c = 0; private int c = 0;
@Override @Override
@@ -405,11 +408,11 @@ public class IrisObject extends IrisRegistrant {
KList<String> palette = new KList<>(); KList<String> palette = new KList<>();
for (BlockData i : getBlocks().values()) { for (BlockData i : blocks.values()) {
palette.addIfMissing(i.getAsString()); palette.addIfMissing(i.getAsString());
++c; ++c;
} }
total -= getBlocks().size() - palette.size(); total -= blocks.size() - palette.size();
dos.writeShort(palette.size()); dos.writeShort(palette.size());
@@ -418,22 +421,24 @@ public class IrisObject extends IrisRegistrant {
++c; ++c;
} }
dos.writeInt(getBlocks().size()); dos.writeInt(blocks.size());
for (Vector3i i : getBlocks().keySet()) { for (var entry : blocks) {
var i = entry.getKey();
dos.writeShort(i.getBlockX()); dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY()); dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ()); dos.writeShort(i.getBlockZ());
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString())); dos.writeShort(palette.indexOf(entry.getValue().getAsString()));
++c; ++c;
} }
dos.writeInt(getStates().size()); dos.writeInt(states.size());
for (Vector3i i : getStates().keySet()) { for (var entry : states) {
var i = entry.getKey();
dos.writeShort(i.getBlockX()); dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY()); dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ()); dos.writeShort(i.getBlockZ());
getStates().get(i).toBinary(dos); entry.getValue().toBinary(dos);
++c; ++c;
} }
} catch (IOException e) { } catch (IOException e) {
@@ -502,7 +507,7 @@ public class IrisObject extends IrisRegistrant {
BlockVector min = new BlockVector(); BlockVector min = new BlockVector();
BlockVector max = new BlockVector(); BlockVector max = new BlockVector();
for (BlockVector i : getBlocks().keySet()) { for (BlockVector i : blocks.keys()) {
min.setX(Math.min(min.getX(), i.getX())); min.setX(Math.min(min.getX(), i.getX()));
min.setY(Math.min(min.getY(), i.getY())); min.setY(Math.min(min.getY(), i.getY()));
min.setZ(Math.min(min.getZ(), i.getZ())); min.setZ(Math.min(min.getZ(), i.getZ()));
@@ -518,21 +523,11 @@ public class IrisObject extends IrisRegistrant {
} }
public void clean() { public void clean() {
KMap<Vector3i, BlockData> d = new KMap<>(); VectorMap<BlockData> d = new VectorMap<>();
d.putAll(blocks);
for (Vector3i i : getBlocks().keySet()) { VectorMap<TileData> dx = new VectorMap<>();
d.put(new Vector3i(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i))); dx.putAll(states);
}
KMap<Vector3i, TileData> dx = new KMap<>();
for (Vector3i i : getBlocks().keySet()) {
d.put(new Vector3i(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i)));
}
for (Vector3i i : getStates().keySet()) {
dx.put(new Vector3i(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getStates().get(i)));
}
blocks = d; blocks = d;
states = dx; states = dx;
@@ -550,10 +545,10 @@ public class IrisObject extends IrisRegistrant {
Vector3i v = getSigned(x, y, z); Vector3i v = getSigned(x, y, z);
if (block == null) { if (block == null) {
getBlocks().remove(v); blocks.remove(v);
getStates().remove(v); states.remove(v);
} else { } else {
getBlocks().put(v, block); blocks.put(v, block);
} }
} }
@@ -561,15 +556,15 @@ public class IrisObject extends IrisRegistrant {
Vector3i v = getSigned(x, y, z); Vector3i v = getSigned(x, y, z);
if (block == null) { if (block == null) {
getBlocks().remove(v); blocks.remove(v);
getStates().remove(v); states.remove(v);
} else { } else {
BlockData data = block.getBlockData(); BlockData data = block.getBlockData();
getBlocks().put(v, data); blocks.put(v, data);
TileData state = TileData.getTileState(block, legacy); TileData state = TileData.getTileState(block, legacy);
if (state != null) { if (state != null) {
Iris.debug("Saved State " + v); Iris.debug("Saved State " + v);
getStates().put(v, state); states.put(v, state);
} }
} }
} }
@@ -866,6 +861,9 @@ public class IrisObject extends IrisRegistrant {
try { try {
if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) { if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) {
markers = new KMap<>(); markers = new KMap<>();
var list = StreamSupport.stream(blocks.keys().spliterator(), false)
.collect(KList.collector());
for (IrisObjectMarker j : config.getMarkers()) { for (IrisObjectMarker j : config.getMarkers()) {
IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker()); IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker());
@@ -874,13 +872,12 @@ public class IrisObject extends IrisRegistrant {
} }
int max = j.getMaximumMarkers(); int max = j.getMaximumMarkers();
for (BlockVector i : list.shuffle()) {
for (Vector3i i : getBlocks().k().shuffle()) {
if (max <= 0) { if (max <= 0) {
break; break;
} }
BlockData data = getBlocks().get(i); BlockData data = blocks.get(i);
for (BlockData k : j.getMark(rdata)) { for (BlockData k : j.getMark(rdata)) {
if (max <= 0) { if (max <= 0) {
@@ -888,8 +885,8 @@ public class IrisObject extends IrisRegistrant {
} }
if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) { if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) {
boolean a = !blocks.containsKey((Vector3i) i.clone().add(new BlockVector(0, 1, 0))); boolean a = !blocks.containsKey((BlockVector) i.clone().add(new BlockVector(0, 1, 0)));
boolean fff = !blocks.containsKey((Vector3i) i.clone().add(new BlockVector(0, 2, 0))); boolean fff = !blocks.containsKey((BlockVector) i.clone().add(new BlockVector(0, 2, 0)));
if (!marker.isEmptyAbove() || (a && fff)) { if (!marker.isEmptyAbove() || (a && fff)) {
markers.put(i, j.getMarker()); markers.put(i, j.getMarker());
@@ -901,14 +898,14 @@ public class IrisObject extends IrisRegistrant {
} }
} }
for (var entry : getBlocks().entrySet()) { for (var entry : blocks) {
var g = entry.getKey(); var g = entry.getKey();
BlockData d; BlockData d;
TileData tile = null; TileData tile = null;
try { try {
d = entry.getValue(); d = entry.getValue();
tile = getStates().get(g); tile = states.get(g);
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)"); Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)");
@@ -1026,12 +1023,12 @@ public class IrisObject extends IrisRegistrant {
if (stilting) { if (stilting) {
readLock.lock(); readLock.lock();
IrisStiltSettings settings = config.getStiltSettings(); IrisStiltSettings settings = config.getStiltSettings();
for (Vector3i g : getBlocks().keySet()) { for (BlockVector g : blocks.keys()) {
BlockData d; BlockData d;
if (settings == null || settings.getPalette() == null) { if (settings == null || settings.getPalette() == null) {
try { try {
d = getBlocks().get(g); d = blocks.get(g);
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)"); Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)");
@@ -1138,58 +1135,60 @@ public class IrisObject extends IrisRegistrant {
} }
public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) { public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) {
KMap<Vector3i, BlockData> d = new KMap<>(); writeLock.lock();
VectorMap<BlockData> d = new VectorMap<>();
for (Vector3i i : getBlocks().keySet()) { for (var entry : blocks) {
d.put(new Vector3i(r.rotate(i, spinx, spiny, spinz)), r.rotate(getBlocks().get(i).clone(), spinx, spiny, spinz)); d.put(r.rotate(entry.getKey(), spinx, spiny, spinz), r.rotate(entry.getValue(), spinx, spiny, spinz));
} }
KMap<Vector3i, TileData> dx = new KMap<>(); VectorMap<TileData> dx = new VectorMap<>();
for (Vector3i i : getStates().keySet()) { for (var entry : states) {
dx.put(new Vector3i(r.rotate(i, spinx, spiny, spinz)), getStates().get(i)); dx.put(r.rotate(entry.getKey(), spinx, spiny, spinz), entry.getValue());
} }
blocks = d; blocks = d;
states = dx; states = dx;
writeLock.unlock();
shrinkwrap(); shrinkwrap();
} }
public void place(Location at) { public void place(Location at) {
for (Vector3i i : getBlocks().keySet()) { readLock.lock();
for (var entry : blocks) {
var i = entry.getKey();
Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock(); Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock();
b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); b.setBlockData(Objects.requireNonNull(entry.getValue()), false);
if (getStates().containsKey(i)) { if (states.containsKey(i)) {
Iris.info(Objects.requireNonNull(states.get(i)).toString()); Iris.info(Objects.requireNonNull(states.get(i)).toString());
Objects.requireNonNull(getStates().get(i)).toBukkitTry(b); Objects.requireNonNull(states.get(i)).toBukkitTry(b);
} }
} }
readLock.unlock();
} }
public void placeCenterY(Location at) { public void placeCenterY(Location at) {
for (Vector3i i : getBlocks().keySet()) { readLock.lock();
for (var entry : blocks) {
var i = entry.getKey();
Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock(); Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock();
b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false); b.setBlockData(Objects.requireNonNull(entry.getValue()), false);
if (getStates().containsKey(i)) { if (states.containsKey(i)) {
Objects.requireNonNull(getStates().get(i)).toBukkitTry(b); Objects.requireNonNull(states.get(i)).toBukkitTry(b);
} }
} }
} readLock.unlock();
public synchronized KMap<Vector3i, BlockData> getBlocks() {
return blocks;
}
public synchronized KMap<Vector3i, TileData> getStates() {
return states;
} }
public void unplaceCenterY(Location at) { public void unplaceCenterY(Location at) {
for (BlockVector i : getBlocks().keySet()) { readLock.lock();
for (BlockVector i : blocks.keys()) {
at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false); at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false);
} }
readLock.unlock();
} }
public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) { public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) {
@@ -1201,7 +1200,7 @@ public class IrisObject extends IrisRegistrant {
IrisPosition l1 = getAABB().max(); IrisPosition l1 = getAABB().max();
IrisPosition l2 = getAABB().min(); IrisPosition l2 = getAABB().min();
@SuppressWarnings({"unchecked", "rawtypes"}) HashMap<BlockVector, BlockData> placeBlock = new HashMap(); VectorMap<BlockData> placeBlock = new VectorMap<>();
Vector center = getCenter(); Vector center = getCenter();
if (getH() == 2) { if (getH() == 2) {
@@ -1216,17 +1215,19 @@ public class IrisObject extends IrisRegistrant {
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))); 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<Vector3i, BlockData> entry : blocks.entrySet()) { readLock.lock();
for (var entry : blocks) {
BlockData bd = entry.getValue(); BlockData bd = entry.getValue();
placeBlock.put(entry.getKey().clone().add(HALF).subtract(center) placeBlock.put(entry.getKey().clone().add(HALF).subtract(center)
.multiply(scale).add(sm1).toBlockVector(), bd); .multiply(scale).add(sm1).toBlockVector(), bd);
} }
readLock.unlock();
for (Map.Entry<BlockVector, BlockData> entry : placeBlock.entrySet()) { for (var entry : placeBlock) {
BlockVector v = entry.getKey(); BlockVector v = entry.getKey();
if (scale > 1) { if (scale > 1) {
for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) { for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) {
oo.getBlocks().put(new Vector3i(vec), entry.getValue()); oo.blocks.put(vec, entry.getValue());
} }
} else { } else {
oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue()); oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue());
@@ -1245,8 +1246,9 @@ public class IrisObject extends IrisRegistrant {
} }
public void trilinear(int rad) { public void trilinear(int rad) {
KMap<Vector3i, BlockData> v = getBlocks().copy(); writeLock.lock();
KMap<Vector3i, BlockData> b = new KMap<>(); VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
BlockVector min = getAABB().minbv(); BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv(); BlockVector max = getAABB().maxbv();
@@ -1254,7 +1256,7 @@ public class IrisObject extends IrisRegistrant {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
if (IrisInterpolation.getTrilinear(x, y, z, rad, (xx, yy, zz) -> { if (IrisInterpolation.getTrilinear(x, y, z, rad, (xx, yy, zz) -> {
BlockData data = v.get(new Vector3i((int) xx, (int) yy, (int) zz)); BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz));
if (data == null || data.getMaterial().isAir()) { if (data == null || data.getMaterial().isAir()) {
return 0; return 0;
@@ -1262,20 +1264,22 @@ public class IrisObject extends IrisRegistrant {
return 1; return 1;
}) >= 0.5) { }) >= 0.5) {
b.put(new Vector3i(x, y, z), nearestBlockData(x, y, z)); b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z));
} else { } else {
b.put(new Vector3i(x, y, z), AIR); b.put(new BlockVector(x, y, z), AIR);
} }
} }
} }
} }
blocks = b; blocks = b;
writeLock.unlock();
} }
public void tricubic(int rad) { public void tricubic(int rad) {
KMap<Vector3i, BlockData> v = getBlocks().copy(); writeLock.lock();
KMap<Vector3i, BlockData> b = new KMap<>(); VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
BlockVector min = getAABB().minbv(); BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv(); BlockVector max = getAABB().maxbv();
@@ -1283,7 +1287,7 @@ public class IrisObject extends IrisRegistrant {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
if (IrisInterpolation.getTricubic(x, y, z, rad, (xx, yy, zz) -> { if (IrisInterpolation.getTricubic(x, y, z, rad, (xx, yy, zz) -> {
BlockData data = v.get(new Vector3i((int) xx, (int) yy, (int) zz)); BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz));
if (data == null || data.getMaterial().isAir()) { if (data == null || data.getMaterial().isAir()) {
return 0; return 0;
@@ -1291,15 +1295,16 @@ public class IrisObject extends IrisRegistrant {
return 1; return 1;
}) >= 0.5) { }) >= 0.5) {
b.put(new Vector3i(x, y, z), nearestBlockData(x, y, z)); b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z));
} else { } else {
b.put(new Vector3i(x, y, z), AIR); b.put(new BlockVector(x, y, z), AIR);
} }
} }
} }
} }
blocks = b; blocks = b;
writeLock.unlock();
} }
public void trihermite(int rad) { public void trihermite(int rad) {
@@ -1307,8 +1312,9 @@ public class IrisObject extends IrisRegistrant {
} }
public void trihermite(int rad, double tension, double bias) { public void trihermite(int rad, double tension, double bias) {
KMap<Vector3i, BlockData> v = getBlocks().copy(); writeLock.lock();
KMap<Vector3i, BlockData> b = new KMap<>(); VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
BlockVector min = getAABB().minbv(); BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv(); BlockVector max = getAABB().maxbv();
@@ -1316,7 +1322,7 @@ public class IrisObject extends IrisRegistrant {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
if (IrisInterpolation.getTrihermite(x, y, z, rad, (xx, yy, zz) -> { if (IrisInterpolation.getTrihermite(x, y, z, rad, (xx, yy, zz) -> {
BlockData data = v.get(new Vector3i((int) xx, (int) yy, (int) zz)); BlockData data = v.get(new BlockVector((int) xx, (int) yy, (int) zz));
if (data == null || data.getMaterial().isAir()) { if (data == null || data.getMaterial().isAir()) {
return 0; return 0;
@@ -1324,20 +1330,22 @@ public class IrisObject extends IrisRegistrant {
return 1; return 1;
}, tension, bias) >= 0.5) { }, tension, bias) >= 0.5) {
b.put(new Vector3i(x, y, z), nearestBlockData(x, y, z)); b.put(new BlockVector(x, y, z), nearestBlockData(x, y, z));
} else { } else {
b.put(new Vector3i(x, y, z), AIR); b.put(new BlockVector(x, y, z), AIR);
} }
} }
} }
} }
blocks = b; blocks = b;
writeLock.unlock();
} }
private BlockData nearestBlockData(int x, int y, int z) { private BlockData nearestBlockData(int x, int y, int z) {
BlockVector vv = new BlockVector(x, y, z); BlockVector vv = new BlockVector(x, y, z);
BlockData r = getBlocks().get(vv); readLock.lock();
BlockData r = blocks.get(vv);
if (r != null && !r.getMaterial().isAir()) { if (r != null && !r.getMaterial().isAir()) {
return r; return r;
@@ -1345,7 +1353,7 @@ public class IrisObject extends IrisRegistrant {
double d = Double.MAX_VALUE; double d = Double.MAX_VALUE;
for (Map.Entry<Vector3i, BlockData> entry : blocks.entrySet()) { for (var entry : blocks) {
BlockData dat = entry.getValue(); BlockData dat = entry.getValue();
if (dat.getMaterial().isAir()) { if (dat.getMaterial().isAir()) {
@@ -1359,6 +1367,7 @@ public class IrisObject extends IrisRegistrant {
r = dat; r = dat;
} }
} }
readLock.unlock();
return r; return r;
} }

View File

@@ -166,6 +166,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
return targetCache.aquire(() -> { return targetCache.aquire(() -> {
IrisData data = IrisData.get(dataLocation); IrisData data = IrisData.get(dataLocation);
data.dump();
data.clearLists();
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) { if (dimension == null) {
@@ -205,7 +207,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
Chunk c = PaperLib.getChunkAtAsync(world, x, z) Chunk c = PaperLib.getChunkAtAsync(world, x, z)
.thenApply(d -> { .thenApply(d -> {
d.addPluginChunkTicket(Iris.instance); Iris.tickets.addTicket(d);
for (Entity ee : d.getEntities()) { for (Entity ee : d.getEntities()) {
if (ee instanceof Player) { if (ee instanceof Player) {
@@ -215,7 +217,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
ee.remove(); ee.remove();
} }
engine.getWorldManager().onChunkLoad(d, false);
return d; return d;
}).get(); }).get();
@@ -241,7 +242,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRunAsync(() -> { .thenRunAsync(() -> {
c.removePluginChunkTicket(Iris.instance); Iris.tickets.removeTicket(c);
engine.getWorldManager().onChunkLoad(c, true); engine.getWorldManager().onChunkLoad(c, true);
}, syncExecutor) }, syncExecutor)
.get(); .get();

View File

@@ -2,24 +2,55 @@ package com.volmit.iris.util.cache;
import com.volmit.iris.util.function.Function2; import com.volmit.iris.util.function.Function2;
import java.util.concurrent.atomic.AtomicReferenceArray; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class ChunkCache2D<T> { public class ChunkCache2D<T> {
private final AtomicReferenceArray<T> cache; private static final boolean FAST = Boolean.getBoolean("iris.cache.fast");
private static final boolean DYNAMIC = Boolean.getBoolean("iris.cache.dynamic");
private static final VarHandle AA = MethodHandles.arrayElementVarHandle(Entry[].class);
private final Entry<T>[] cache;
@SuppressWarnings({"unchecked"})
public ChunkCache2D() { public ChunkCache2D() {
this.cache = new AtomicReferenceArray<>(256); this.cache = new Entry[256];
if (DYNAMIC) return;
for (int i = 0; i < cache.length; i++) {
cache[i] = FAST ? new FastEntry<>() : new Entry<>();
}
} }
@SuppressWarnings({"unchecked"})
public T get(int x, int z, Function2<Integer, Integer, T> resolver) { public T get(int x, int z, Function2<Integer, Integer, T> resolver) {
int key = ((z & 15) * 16) + (x & 15); int key = ((z & 15) * 16) + (x & 15);
T t = cache.get(key); var entry = cache[key];
if (entry == null) {
if (t == null) { entry = FAST ? new FastEntry<>() : new Entry<>();
t = resolver.apply(x, z); if (!AA.compareAndSet(cache, key, null, entry)) {
cache.set(key, t); entry = (Entry<T>) AA.getVolatile(cache, key);
}
}
return entry.compute(x, z, resolver);
} }
private static class Entry<T> {
protected volatile T t;
protected T compute(int x, int z, Function2<Integer, Integer, T> resolver) {
if (t != null) return t;
synchronized (this) {
if (t == null) t = resolver.apply(x, z);
return t; return t;
} }
} }
}
private static class FastEntry<T> extends Entry<T> {
@Override
protected T compute(int x, int z, Function2<Integer, Integer, T> resolver) {
if (t != null) return t;
return t = resolver.apply(x, z);
}
}
}

View File

@@ -1,24 +1,32 @@
package com.volmit.iris.util.cache; package com.volmit.iris.util.cache;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.function.Function2; import com.volmit.iris.util.function.Function2;
public class WorldCache2D<T> { public class WorldCache2D<T> {
private final KCache<Long, ChunkCache2D<T>> chunks; private final ConcurrentLinkedHashMap<Long, ChunkCache2D<T>> chunks;
private final Function2<Integer, Integer, T> resolver; private final Function2<Integer, Integer, T> resolver;
public WorldCache2D(Function2<Integer, Integer, T> resolver) { public WorldCache2D(Function2<Integer, Integer, T> resolver, int size) {
this.resolver = resolver; this.resolver = resolver;
chunks = new KCache<>((x) -> new ChunkCache2D<>(), 1024); chunks = new ConcurrentLinkedHashMap.Builder<Long, ChunkCache2D<T>>()
.initialCapacity(size)
.maximumWeightedCapacity(size)
.concurrencyLevel(Math.max(32, Runtime.getRuntime().availableProcessors() * 4))
.build();
} }
public T get(int x, int z) { public T get(int x, int z) {
ChunkCache2D<T> chunk = chunks.get(Cache.key(x >> 4, z >> 4)); ChunkCache2D<T> chunk = chunks.computeIfAbsent(Cache.key(x >> 4, z >> 4), $ -> new ChunkCache2D<>());
return chunk.get(x, z, resolver); return chunk.get(x, z, resolver);
} }
public long getSize() { public long getSize() {
return chunks.getSize() * 256L; return chunks.size() * 256L;
}
public long getMaxSize() {
return chunks.capacity() * 256L;
} }
} }

View File

@@ -33,7 +33,15 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 7288942695300448163L; private static final long serialVersionUID = 7288942695300448163L;
public KMap() { public KMap() {
super(); this(16);
}
public KMap(int initialCapacity) {
this(initialCapacity, 0.75f, 1);
}
public KMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
} }
public KMap(Map<K, V> gMap) { public KMap(Map<K, V> gMap) {

View File

@@ -1,51 +0,0 @@
package com.volmit.iris.util.context;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisRegion;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import lombok.Data;
import org.bukkit.block.data.BlockData;
@Data
public class ChunkContext {
private final int x;
private final int z;
private ChunkedDataCache<Double> height;
private ChunkedDataCache<IrisBiome> biome;
private ChunkedDataCache<IrisBiome> cave;
private ChunkedDataCache<BlockData> rock;
private ChunkedDataCache<BlockData> fluid;
private ChunkedDataCache<IrisRegion> region;
@BlockCoordinates
public ChunkContext(int x, int z, IrisComplex c) {
this(x, z, c, true);
}
@BlockCoordinates
public ChunkContext(int x, int z, IrisComplex c, boolean cache) {
this.x = x;
this.z = z;
if (cache) {
BurstExecutor b = MultiBurst.burst.burst();
height = new ChunkedDataCache<>(b, c.getHeightStream(), x, z);
biome = new ChunkedDataCache<>(b, c.getTrueBiomeStream(), x, z);
cave = new ChunkedDataCache<>(b, c.getCaveBiomeStream(), x, z);
rock = new ChunkedDataCache<>(b, c.getRockStream(), x, z);
fluid = new ChunkedDataCache<>(b, c.getFluidStream(), x, z);
region = new ChunkedDataCache<>(b, c.getRegionStream(), x, z);
b.complete();
} else {
height = new ChunkedDataCache<>(null, c.getHeightStream(), x, z, false);
biome = new ChunkedDataCache<>(null, c.getTrueBiomeStream(), x, z, false);
cave = new ChunkedDataCache<>(null, c.getCaveBiomeStream(), x, z, false);
rock = new ChunkedDataCache<>(null, c.getRockStream(), x, z, false);
fluid = new ChunkedDataCache<>(null, c.getFluidStream(), x, z, false);
region = new ChunkedDataCache<>(null, c.getRegionStream(), x, z, false);
}
}
}

View File

@@ -1,60 +0,0 @@
package com.volmit.iris.util.context;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.stream.ProceduralStream;
import lombok.Data;
@Data
public class ChunkedDataCache<T> {
private final int x;
private final int z;
private final KSet<T> uniques;
private final Object[] data;
private final boolean cache;
private final ProceduralStream<T> stream;
@BlockCoordinates
public ChunkedDataCache(BurstExecutor burst, ProceduralStream<T> stream, int x, int z) {
this(burst, stream, x, z, true);
}
@BlockCoordinates
public ChunkedDataCache(BurstExecutor burst, ProceduralStream<T> stream, int x, int z, boolean cache) {
this.stream = stream;
this.cache = cache;
this.x = x;
this.z = z;
this.uniques = cache ? new KSet<>() : null;
if (cache) {
data = new Object[256];
int i, j;
for (i = 0; i < 16; i++) {
int finalI = i;
for (j = 0; j < 16; j++) {
int finalJ = j;
burst.queue(() -> {
T t = stream.get(x + finalI, z + finalJ);
data[(finalJ * 16) + finalI] = t;
uniques.add(t);
});
}
}
} else {
data = new Object[0];
}
}
@SuppressWarnings("unchecked")
@BlockCoordinates
public T get(int x, int z) {
if (!cache) {
return stream.get(this.x + x, this.z + z);
}
T t = (T) data[(z * 16) + x];
return t == null ? stream.get(this.x + x, this.z + z) : t;
}
}

View File

@@ -1,127 +1,77 @@
package com.volmit.iris.util.data; package com.volmit.iris.util.data;
import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.link.Identifier;
import lombok.Data; import com.volmit.iris.util.collection.KMap;
import lombok.NonNull; 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.data.BlockData;
import org.bukkit.block.structure.Mirror; import org.jetbrains.annotations.ApiStatus;
import org.bukkit.block.structure.StructureRotation;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Data import java.lang.reflect.Proxy;
public class IrisCustomData implements BlockData { import java.util.*;
private final @NonNull BlockData base;
private final @NotNull Identifier custom;
@NotNull public interface IrisCustomData extends BlockData {
@Override @NonNull BlockData getBase();
public Material getMaterial() { @NonNull Identifier getCustom();
return base.getMaterial();
static IrisCustomData of(@NotNull BlockData base, @NotNull Identifier custom) {
var clazz = base.getClass();
var loader = clazz.getClassLoader();
return (IrisCustomData) Proxy.newProxyInstance(loader, Internal.getInterfaces(loader, clazz), (proxy, method, args) ->
switch (method.getName()) {
case "getBase" -> base;
case "getCustom" -> custom;
case "merge" -> of(base.merge((BlockData) args[0]), custom);
case "clone" -> of(base.clone(), custom);
case "hashCode" -> Objects.hash(base, custom);
case "matches" -> {
if (!(args[0] instanceof IrisCustomData store))
yield false;
yield base.matches(store.getBase()) && custom.equals(store.getCustom());
}
case "equals" -> {
if (!(args[0] instanceof IrisCustomData store))
yield false;
yield store.getBase().equals(base) && store.getCustom().equals(custom);
}
default -> method.invoke(base, args);
});
} }
@NotNull @ApiStatus.Internal
@Override abstract class Internal {
public String getAsString() { private static final KMap<Class<?>, Class<?>[]> cache = new KMap<>();
return base.getAsString();
private static Class<?>[] getInterfaces(ClassLoader loader, Class<?> base) {
return cache.computeIfAbsent(base, k -> {
Queue<Class<?>> queue = new LinkedList<>();
Set<Class<?>> set = new HashSet<>();
queue.add(k);
while (!queue.isEmpty()) {
Class<?> i = queue.poll();
if (!BlockData.class.isAssignableFrom(i))
continue;
for (Class<?> j : i.getInterfaces()) {
if (j.isSealed() || j.isHidden())
continue;
try {
Class.forName(j.getName(), false, loader);
set.add(j);
} catch (ClassNotFoundException ignored) {}
} }
@NotNull var parent = i.getSuperclass();
@Override if (parent != null)
public String getAsString(boolean b) { queue.add(parent);
return base.getAsString(b);
} }
@NotNull set.add(IrisCustomData.class);
@Override return set.toArray(Class<?>[]::new);
public BlockData merge(@NotNull BlockData blockData) { });
return new IrisCustomData(base.merge(blockData), custom); }
}
@Override
public boolean matches(@Nullable BlockData blockData) {
if (blockData instanceof IrisCustomData b)
return custom.equals(b.custom) && base.matches(b.base);
return base.matches(blockData);
}
@NotNull
@Override
public BlockData clone() {
return new IrisCustomData(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();
} }
} }

View File

@@ -0,0 +1,220 @@
package com.volmit.iris.util.data;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull;
import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class VectorMap<T> implements Iterable<Map.Entry<BlockVector, T>> {
private final Map<Key, Map<Key, T>> map = new KMap<>();
public int size() {
return map.values().stream().mapToInt(Map::size).sum();
}
public boolean isEmpty() {
return map.values().stream().allMatch(Map::isEmpty);
}
public boolean containsKey(@NonNull BlockVector vector) {
var chunk = map.get(chunk(vector));
return chunk != null && chunk.containsKey(relative(vector));
}
public boolean containsValue(@NonNull T value) {
return map.values().stream().anyMatch(m -> m.containsValue(value));
}
public @Nullable T get(@NonNull BlockVector vector) {
var chunk = map.get(chunk(vector));
return chunk == null ? null : chunk.get(relative(vector));
}
public @Nullable T put(@NonNull BlockVector vector, @NonNull T value) {
return map.computeIfAbsent(chunk(vector), k -> new KMap<>())
.put(relative(vector), value);
}
public @Nullable T computeIfAbsent(@NonNull BlockVector vector, @NonNull Function<@NonNull BlockVector, @NonNull T> mappingFunction) {
return map.computeIfAbsent(chunk(vector), k -> new KMap<>())
.computeIfAbsent(relative(vector), $ -> mappingFunction.apply(vector));
}
public @Nullable T remove(@NonNull BlockVector vector) {
var chunk = map.get(chunk(vector));
return chunk == null ? null : chunk.remove(relative(vector));
}
public void putAll(@NonNull VectorMap<T> map) {
map.forEach(this::put);
}
public void clear() {
map.clear();
}
public void forEach(@NonNull BiConsumer<@NonNull BlockVector, @NonNull T> consumer) {
map.forEach((chunk, values) -> {
int rX = chunk.x << 10;
int rY = chunk.y << 10;
int rZ = chunk.z << 10;
values.forEach((relative, value) -> consumer.accept(
relative.resolve(rX, rY, rZ),
value
));
});
}
private static Key chunk(BlockVector vector) {
return new Key(vector.getBlockX() >> 10, vector.getBlockY() >> 10, vector.getBlockZ() >> 10);
}
private static Key relative(BlockVector vector) {
return new Key(vector.getBlockX() & 0x3FF, vector.getBlockY() & 0x3FF, vector.getBlockZ() & 0x3FF);
}
@Override
public @NotNull EntryIterator iterator() {
return new EntryIterator();
}
public @NotNull KeyIterator keys() {
return new KeyIterator();
}
public @NotNull ValueIterator values() {
return new ValueIterator();
}
public class EntryIterator implements Iterator<Map.Entry<BlockVector, T>> {
private final Iterator<Map.Entry<Key, Map<Key, T>>> chunkIterator = map.entrySet().iterator();
private Iterator<Map.Entry<Key, T>> relativeIterator;
private int rX, rY, rZ;
@Override
public boolean hasNext() {
return relativeIterator != null && relativeIterator.hasNext() || chunkIterator.hasNext();
}
@Override
public Map.Entry<BlockVector, T> next() {
if (relativeIterator == null || !relativeIterator.hasNext()) {
if (!chunkIterator.hasNext()) throw new IllegalStateException("No more elements");
var chunk = chunkIterator.next();
rX = chunk.getKey().x << 10;
rY = chunk.getKey().y << 10;
rZ = chunk.getKey().z << 10;
relativeIterator = chunk.getValue().entrySet().iterator();
}
var entry = relativeIterator.next();
return Map.entry(entry.getKey().resolve(rX, rY, rZ), entry.getValue());
}
@Override
public void remove() {
if (relativeIterator == null) throw new IllegalStateException("No element to remove");
relativeIterator.remove();
}
}
public class KeyIterator implements Iterator<BlockVector>, Iterable<BlockVector> {
private final Iterator<Map.Entry<Key, Map<Key, T>>> chunkIterator = map.entrySet().iterator();
private Iterator<Key> relativeIterator;
private int rX, rY, rZ;
@Override
public boolean hasNext() {
return relativeIterator != null && relativeIterator.hasNext() || chunkIterator.hasNext();
}
@Override
public BlockVector next() {
if (relativeIterator == null || !relativeIterator.hasNext()) {
var chunk = chunkIterator.next();
rX = chunk.getKey().x << 10;
rY = chunk.getKey().y << 10;
rZ = chunk.getKey().z << 10;
relativeIterator = chunk.getValue().keySet().iterator();
}
return relativeIterator.next().resolve(rX, rY, rZ);
}
@Override
public void remove() {
if (relativeIterator == null) throw new IllegalStateException("No element to remove");
relativeIterator.remove();
}
@Override
public @NotNull Iterator<BlockVector> iterator() {
return this;
}
}
public class ValueIterator implements Iterator<T>, Iterable<T> {
private final Iterator<Map<Key, T>> chunkIterator = map.values().iterator();
private Iterator<T> relativeIterator;
@Override
public boolean hasNext() {
return relativeIterator != null && relativeIterator.hasNext() || chunkIterator.hasNext();
}
@Override
public T next() {
if (relativeIterator == null || !relativeIterator.hasNext()) {
relativeIterator = chunkIterator.next().values().iterator();
}
return relativeIterator.next();
}
@Override
public void remove() {
if (relativeIterator == null) throw new IllegalStateException("No element to remove");
relativeIterator.remove();
}
@Override
public @NotNull Iterator<T> iterator() {
return this;
}
}
private static final class Key {
private final int x;
private final int y;
private final int z;
private final int hashCode;
private Key(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
this.hashCode = (x << 20) | (y << 10) | z;
}
private BlockVector resolve(int rX, int rY, int rZ) {
return new BlockVector(rX + x, rY + y, rZ + z);
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Key key)) return false;
return x == key.x && y == key.y && z == key.z;
}
}
}

View File

@@ -19,21 +19,30 @@
package com.volmit.iris.util.hunk.bits; package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.data.Varint;
import lombok.Synchronized; import it.unimi.dsi.fastutil.ints.*;
import java.io.*; import java.io.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class DataContainer<T> { public class DataContainer<T> {
private static final boolean TRIM = Boolean.getBoolean("iris.trim-palette");
protected static final int INITIAL_BITS = 3; protected static final int INITIAL_BITS = 3;
protected static final int LINEAR_BITS_LIMIT = 4; protected static final int LINEAR_BITS_LIMIT = 4;
protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1; protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 2;
protected static final int[] BIT = computeBitLimits(); protected static final int[] BIT = computeBitLimits();
private final Lock read, write;
private volatile Palette<T> palette; private volatile Palette<T> palette;
private volatile DataBits data; private volatile DataBits data;
private final int length; private final int length;
private final Writable<T> writer; private final Writable<T> writer;
public DataContainer(Writable<T> writer, int length) { public DataContainer(Writable<T> writer, int length) {
var lock = new ReentrantReadWriteLock();
this.read = lock.readLock();
this.write = lock.writeLock();
this.writer = writer; this.writer = writer;
this.length = length; this.length = length;
this.data = new DataBits(INITIAL_BITS, length); this.data = new DataBits(INITIAL_BITS, length);
@@ -41,10 +50,15 @@ public class DataContainer<T> {
} }
public DataContainer(DataInputStream din, Writable<T> writer) throws IOException { public DataContainer(DataInputStream din, Writable<T> writer) throws IOException {
var lock = new ReentrantReadWriteLock();
this.read = lock.readLock();
this.write = lock.writeLock();
this.writer = writer; this.writer = writer;
this.length = Varint.readUnsignedVarInt(din); this.length = Varint.readUnsignedVarInt(din);
this.palette = newPalette(din); this.palette = newPalette(din);
this.data = new DataBits(palette.bits(), length, din); this.data = new DataBits(palette.bits(), length, din);
trim();
} }
private static int[] computeBitLimits() { private static int[] computeBitLimits() {
@@ -86,13 +100,18 @@ public class DataContainer<T> {
writeDos(new DataOutputStream(out)); writeDos(new DataOutputStream(out));
} }
@Synchronized
public void writeDos(DataOutputStream dos) throws IOException { public void writeDos(DataOutputStream dos) throws IOException {
write.lock();
try {
trim();
Varint.writeUnsignedVarInt(length, dos); Varint.writeUnsignedVarInt(length, dos);
Varint.writeUnsignedVarInt(palette.size(), dos); Varint.writeUnsignedVarInt(palette.size(), dos);
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data)); palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
data.write(dos); data.write(dos);
dos.flush(); dos.flush();
} finally {
write.unlock();
}
} }
private Palette<T> newPalette(DataInputStream din) throws IOException { private Palette<T> newPalette(DataInputStream din) throws IOException {
@@ -110,25 +129,38 @@ public class DataContainer<T> {
return new HashPalette<>(); return new HashPalette<>();
} }
@Synchronized
public void set(int position, T t) { public void set(int position, T t) {
int id = palette.id(t); int id;
read.lock();
try {
id = palette.id(t);
if (id == -1) { if (id == -1) {
id = palette.add(t); id = palette.add(t);
updateBits(); if (palette.bits() == data.getBits()) {
}
data.set(position, id); data.set(position, id);
return;
}
}
} finally {
read.unlock();
}
write.lock();
try {
updateBits();
data.set(position, id);
} finally {
write.unlock();
}
} }
@Synchronized
@SuppressWarnings("NonAtomicOperationOnVolatileField") @SuppressWarnings("NonAtomicOperationOnVolatileField")
private void updateBits() { private void updateBits() {
if (palette.bits() == data.getBits()) int bits = palette.bits();
if (bits == data.getBits())
return; return;
int bits = palette.bits();
if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) { if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
palette = newPalette(bits).from(palette); palette = newPalette(bits).from(palette);
} }
@@ -136,8 +168,9 @@ public class DataContainer<T> {
data = data.setBits(bits); data = data.setBits(bits);
} }
@Synchronized
public T get(int position) { public T get(int position) {
read.lock();
try {
int id = data.get(position); int id = data.get(position);
if (id <= 0) { if (id <= 0) {
@@ -145,9 +178,34 @@ public class DataContainer<T> {
} }
return palette.get(id); return palette.get(id);
} finally {
read.unlock();
}
} }
public int size() { public int size() {
return data.getSize(); return data.getSize();
} }
private void trim() {
var ints = new Int2IntRBTreeMap();
for (int i = 0; i < length; i++) {
int x = data.get(i);
if (x <= 0) continue;
ints.put(x, x);
}
if (ints.size() == palette.size())
return;
int bits = bits(ints.size() + 1);
var trimmed = newPalette(bits);
ints.replaceAll((k, v) -> trimmed.add(palette.get(k)));
var tBits = new DataBits(bits, length);
for (int i = 0; i < length; i++) {
tBits.set(i, ints.get(data.get(i)));
}
data = tBits;
palette = trimmed;
}
} }

View File

@@ -27,13 +27,14 @@ import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public class HashPalette<T> implements Palette<T> { public class HashPalette<T> implements Palette<T> {
private final LinkedHashMap<T, Integer> palette; private final Object lock = new Object();
private final KMap<T, Integer> palette;
private final KMap<Integer, T> lookup; private final KMap<Integer, T> lookup;
private final AtomicInteger size; private final AtomicInteger size;
public HashPalette() { public HashPalette() {
this.size = new AtomicInteger(1); this.size = new AtomicInteger(1);
this.palette = new LinkedHashMap<>(); this.palette = new KMap<>();
this.lookup = new KMap<>(); this.lookup = new KMap<>();
} }
@@ -52,13 +53,13 @@ public class HashPalette<T> implements Palette<T> {
return 0; return 0;
} }
synchronized (palette) {
return palette.computeIfAbsent(t, $ -> { return palette.computeIfAbsent(t, $ -> {
synchronized (lock) {
int index = size.getAndIncrement(); int index = size.getAndIncrement();
lookup.put(index, t); lookup.put(index, t);
return index; return index;
});
} }
});
} }
@Override @Override
@@ -78,7 +79,7 @@ public class HashPalette<T> implements Palette<T> {
@Override @Override
public void iterate(Consumer2<T, Integer> c) { public void iterate(Consumer2<T, Integer> c) {
synchronized (palette) { synchronized (lock) {
for (int i = 1; i < size.get(); i++) { for (int i = 1; i < size.get(); i++) {
c.accept(lookup.get(i), i); c.accept(lookup.get(i), i);
} }

View File

@@ -45,26 +45,24 @@ public class LinearPalette<T> implements Palette<T> {
@Override @Override
public int add(T t) { public int add(T t) {
if (t == null) {
return 0;
}
int index = size.getAndIncrement(); int index = size.getAndIncrement();
grow(index + 1); if (palette.length() <= index)
grow(index);
palette.set(index, t); palette.set(index, t);
return index; return index;
} }
private synchronized void grow(int newLength) { private synchronized void grow(int lastIndex) {
if (newLength > palette.length()) { if (palette.length() > lastIndex)
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength); return;
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(lastIndex + 1);
for (int i = 0; i < palette.length(); i++) { for (int i = 0; i < palette.length(); i++) {
a.set(i, palette.get(i)); a.set(i, palette.get(i));
} }
palette = a; palette = a;
} }
}
@Override @Override
public int id(T t) { public int id(T t) {

View File

@@ -999,7 +999,7 @@ public class IrisInterpolation {
public static double getNoise(InterpolationMethod method, int x, int z, double h, NoiseProvider noise) { public static double getNoise(InterpolationMethod method, int x, int z, double h, NoiseProvider noise) {
HashMap<NoiseKey, Double> cache = new HashMap<>(64); HashMap<NoiseKey, Double> cache = new HashMap<>(64);
NoiseProvider n = (x1, z1) -> cache.computeIfAbsent(new NoiseKey(x1, z1), k -> noise.noise(k.x, k.z)); NoiseProvider n = (x1, z1) -> cache.computeIfAbsent(new NoiseKey(x1 - x, z1 - z), k -> noise.noise(x1, z1));
if (method.equals(InterpolationMethod.BILINEAR)) { if (method.equals(InterpolationMethod.BILINEAR)) {
return getBilinearNoise(x, z, h, n); return getBilinearNoise(x, z, h, n);

View File

@@ -176,8 +176,8 @@ public class Mantle {
* @return the writer * @return the writer
*/ */
@ChunkCoordinates @ChunkCoordinates
public MantleWriter write(EngineMantle engineMantle, int x, int z, int radius) { public MantleWriter write(EngineMantle engineMantle, int x, int z, int radius, boolean multicore) {
return new MantleWriter(engineMantle, this, x, z, radius); return new MantleWriter(engineMantle, this, x, z, radius, multicore);
} }
/** /**
@@ -406,18 +406,6 @@ public class Mantle {
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
} }
/**
* Estimates the memory usage of the lastUse map.
*
* @return Estimated memory usage in bytes.
*/
public long LastUseMapMemoryUsage() {
long numberOfEntries = lastUse.size();
long bytesPerEntry = Long.BYTES * 2;
return numberOfEntries * bytesPerEntry;
}
/** /**
* Save & unload regions that have not been used for more than the * Save & unload regions that have not been used for more than the
* specified amount of milliseconds * specified amount of milliseconds
@@ -635,7 +623,7 @@ public class Mantle {
} }
public void deleteChunkSlice(int x, int z, Class<?> c) { public void deleteChunkSlice(int x, int z, Class<?> c) {
if (!IrisToolbelt.toolbeltConfiguration.isEmpty() && IrisToolbelt.toolbeltConfiguration.getOrDefault("retain.mantle." + c.getCanonicalName(), false)) { if (IrisToolbelt.isRetainingMantleDataForSlice(c.getCanonicalName())) {
return; return;
} }

View File

@@ -19,16 +19,14 @@
package com.volmit.iris.util.mantle; package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.util.data.Varint; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates; import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates;
import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.matter.IrisMatter; import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice; import com.volmit.iris.util.matter.MatterSlice;
import com.volmit.iris.util.parallel.AtomicBooleanArray;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -44,13 +42,11 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
* Represents a mantle chunk. Mantle chunks contain sections of matter (see matter api) * Represents a mantle chunk. Mantle chunks contain sections of matter (see matter api)
* Mantle Chunks are fully atomic & thread safe * Mantle Chunks are fully atomic & thread safe
*/ */
public class MantleChunk { public class MantleChunk extends FlaggedChunk {
@Getter @Getter
private final int x; private final int x;
@Getter @Getter
private final int z; private final int z;
private final AtomicBooleanArray flags;
private final Object[] flagLocks;
private final AtomicReferenceArray<Matter> sections; private final AtomicReferenceArray<Matter> sections;
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true); private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean closed = new AtomicBoolean(false);
@@ -63,14 +59,8 @@ public class MantleChunk {
@ChunkCoordinates @ChunkCoordinates
public MantleChunk(int sectionHeight, int x, int z) { public MantleChunk(int sectionHeight, int x, int z) {
sections = new AtomicReferenceArray<>(sectionHeight); sections = new AtomicReferenceArray<>(sectionHeight);
flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1);
flagLocks = new Object[flags.length()];
this.x = x; this.x = x;
this.z = z; this.z = z;
for (int i = 0; i < flags.length(); i++) {
flagLocks[i] = new Object();
}
} }
/** /**
@@ -84,20 +74,7 @@ public class MantleChunk {
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
this(sectionHeight, din.readByte(), din.readByte()); this(sectionHeight, din.readByte(), din.readByte());
int s = din.readByte(); int s = din.readByte();
int l = version < 0 ? 16 : Varint.readUnsignedVarInt(din); readFlags(version, din);
if (version >= 1) {
for (int i = 0; i < l;) {
byte f = din.readByte();
for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) {
flags.set(i, (f & (1 << j)) != 0);
}
}
} else {
for (int i = 0; i < flags.length() && i < l; i++) {
flags.set(i, din.readBoolean());
}
}
for (int i = 0; i < s; i++) { for (int i = 0; i < s; i++) {
Iris.addPanic("read.section", "Section[" + i + "]"); Iris.addPanic("read.section", "Section[" + i + "]");
@@ -156,52 +133,10 @@ public class MantleChunk {
public void copyFlags(MantleChunk chunk) { public void copyFlags(MantleChunk chunk) {
use(); use();
for (int i = 0; i < flags.length(); i++) { super.copyFlags(chunk);
flags.set(i, chunk.flags.get(i));
}
release(); release();
} }
public void flag(MantleFlag flag, boolean f) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
flags.set(flag.ordinal(), f);
}
public void raiseFlag(MantleFlag flag, Runnable r) {
raiseFlag(null, flag, r);
}
public void raiseFlag(@Nullable MantleFlag guard, MantleFlag flag, Runnable r) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
if (guard != null && isFlagged(guard)) return;
synchronized (flagLocks[flag.ordinal()]) {
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
r.run();
} catch (RuntimeException | Error e) {
flags.set(flag.ordinal(), false);
throw e;
}
}
}
}
public void raiseFlagUnchecked(MantleFlag flag, Runnable r) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
r.run();
} catch (RuntimeException | Error e) {
flags.set(flag.ordinal(), false);
throw e;
}
}
}
public boolean isFlagged(MantleFlag flag) {
return flags.get(flag.ordinal());
}
/** /**
* Check if a section exists (same as get(section) != null) * Check if a section exists (same as get(section) != null)
* *
@@ -283,16 +218,7 @@ public class MantleChunk {
dos.writeByte(x); dos.writeByte(x);
dos.writeByte(z); dos.writeByte(z);
dos.writeByte(sections.length()); dos.writeByte(sections.length());
Varint.writeUnsignedVarInt(flags.length(), dos); writeFlags(dos);
int count = flags.length();
for (int i = 0; i < count;) {
int f = 0;
for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) {
f |= flags.get(i) ? (1 << j) : 0;
}
dos.write(f);
}
var bytes = new ByteArrayOutputStream(8192); var bytes = new ByteArrayOutputStream(8192);
var sub = new DataOutputStream(bytes); var sub = new DataOutputStream(bytes);
@@ -345,6 +271,8 @@ public class MantleChunk {
} }
public void deleteSlices(Class<?> c) { public void deleteSlices(Class<?> c) {
if (IrisToolbelt.isRetainingMantleDataForSlice(c.getCanonicalName()))
return;
for (int i = 0; i < sections.length(); i++) { for (int i = 0; i < sections.length(); i++) {
Matter m = sections.get(i); Matter m = sections.get(i);
if (m != null && m.hasSlice(c)) { if (m != null && m.hasSlice(c)) {
@@ -360,4 +288,9 @@ public class MantleChunk {
} }
} }
} }
@Override
public boolean isClosed() {
return closed.get();
}
} }

View File

@@ -21,6 +21,6 @@ public class Vector3i extends BlockVector {
@Override @Override
public int hashCode() { public int hashCode() {
return (((int) x & 0x3FF) << 2) | (((int) y & 0x3FF) << 1) | ((int) z & 0x3FF); return (int) x ^ ((int) z << 12) ^ ((int) y << 24);
} }
} }

View File

@@ -85,13 +85,13 @@ public interface Matter {
BlockVector min = new BlockVector(); BlockVector min = new BlockVector();
Matter m = new IrisMatter(Math.max(object.getW(), 1) + 1, Math.max(object.getH(), 1) + 1, Math.max(object.getD(), 1) + 1); Matter m = new IrisMatter(Math.max(object.getW(), 1) + 1, Math.max(object.getH(), 1) + 1, Math.max(object.getD(), 1) + 1);
for (BlockVector i : object.getBlocks().keySet()) { for (BlockVector i : object.getBlocks().keys()) {
min.setX(Math.min(min.getX(), i.getX())); min.setX(Math.min(min.getX(), i.getX()));
min.setY(Math.min(min.getY(), i.getY())); min.setY(Math.min(min.getY(), i.getY()));
min.setZ(Math.min(min.getZ(), i.getZ())); min.setZ(Math.min(min.getZ(), i.getZ()));
} }
for (BlockVector i : object.getBlocks().keySet()) { for (BlockVector i : object.getBlocks().keys()) {
m.slice(BlockData.class).set(i.getBlockX() - min.getBlockX(), i.getBlockY() - min.getBlockY(), i.getBlockZ() - min.getBlockZ(), object.getBlocks().get(i)); m.slice(BlockData.class).set(i.getBlockX() - min.getBlockX(), i.getBlockY() - min.getBlockY(), i.getBlockZ() - min.getBlockZ(), object.getBlocks().get(i));
} }

View File

@@ -23,6 +23,8 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.ExecutorsKt;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Collection; import java.util.Collection;
@@ -39,7 +41,9 @@ public class MultiBurst implements ExecutorService {
private final String name; private final String name;
private final int priority; private final int priority;
private final IntSupplier parallelism; private final IntSupplier parallelism;
private ExecutorService service; private final Object lock = new Object();
private volatile ExecutorService service;
private volatile CoroutineDispatcher dispatcher;
public MultiBurst() { public MultiBurst() {
this("Iris"); this("Iris");
@@ -60,9 +64,15 @@ public class MultiBurst implements ExecutorService {
last = new AtomicLong(M.ms()); last = new AtomicLong(M.ms());
} }
private synchronized ExecutorService getService() { private ExecutorService getService() {
last.set(M.ms()); last.set(M.ms());
if (service == null || service.isShutdown()) { if (service != null && !service.isShutdown())
return service;
synchronized (lock) {
if (service != null && !service.isShutdown())
return service;
service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()), service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()),
new ForkJoinPool.ForkJoinWorkerThreadFactory() { new ForkJoinPool.ForkJoinWorkerThreadFactory() {
int m = 0; int m = 0;
@@ -76,9 +86,14 @@ public class MultiBurst implements ExecutorService {
} }
}, },
(t, e) -> e.printStackTrace(), true); (t, e) -> e.printStackTrace(), true);
dispatcher = ExecutorsKt.from(service);
return service;
}
} }
return service; public CoroutineDispatcher getDispatcher() {
getService();
return dispatcher;
} }
public void burst(Runnable... r) { public void burst(Runnable... r) {

View File

@@ -0,0 +1,57 @@
package com.volmit.iris.util.plugin.chunk;
import com.volmit.iris.Iris;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import java.util.HashMap;
import java.util.Map;
public class ChunkTickets implements Listener {
private final Map<World, TicketHolder> holders = new HashMap<>();
public ChunkTickets() {
Iris.instance.registerListener(this);
Bukkit.getWorlds().forEach(w -> holders.put(w, new TicketHolder(w)));
}
public TicketHolder getHolder(@NonNull World world) {
return holders.get(world);
}
public void addTicket(@NonNull Chunk chunk) {
addTicket(chunk.getWorld(), chunk.getX(), chunk.getZ());
}
public void addTicket(@NonNull World world, int x, int z) {
var holder = getHolder(world);
if (holder != null) holder.addTicket(x, z);
}
public boolean removeTicket(@NonNull Chunk chunk) {
return removeTicket(chunk.getWorld(), chunk.getX(), chunk.getZ());
}
public boolean removeTicket(@NonNull World world, int x, int z) {
var holder = getHolder(world);
if (holder != null) return holder.removeTicket(x, z);
return false;
}
@EventHandler(priority = EventPriority.LOWEST)
public void on(@NonNull WorldLoadEvent event) {
holders.put(event.getWorld(), new TicketHolder(event.getWorld()));
}
@EventHandler(priority = EventPriority.MONITOR)
public void on(@NonNull WorldUnloadEvent event) {
holders.remove(event.getWorld());
}
}

View File

@@ -0,0 +1,48 @@
package com.volmit.iris.util.plugin.chunk;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull;
import org.bukkit.Chunk;
import org.bukkit.World;
public class TicketHolder {
private final World world;
private final KMap<Long, Long> tickets = new KMap<>();
public TicketHolder(@NonNull World world) {
this.world = world;
}
public void addTicket(@NonNull Chunk chunk) {
if (chunk.getWorld() != world) return;
addTicket(chunk.getX(), chunk.getZ());
}
public void addTicket(int x, int z) {
tickets.compute(Cache.key(x, z), ($, ref) -> {
if (ref == null) {
world.addPluginChunkTicket(x, z, Iris.instance);
return 1L;
}
return ++ref;
});
}
public boolean removeTicket(@NonNull Chunk chunk) {
if (chunk.getWorld() != world) return false;
return removeTicket(chunk.getX(), chunk.getZ());
}
public boolean removeTicket(int x, int z) {
return tickets.compute(Cache.key(x, z), ($, ref) -> {
if (ref == null) return null;
if (--ref <= 0) {
world.removePluginChunkTicket(x, z, Iris.instance);
return null;
}
return ref;
}) == null;
}
}

View File

@@ -0,0 +1,86 @@
package com.volmit.iris.util.scheduling.jobs;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.parallel.MultiBurst;
import lombok.SneakyThrows;
import lombok.Synchronized;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class ParallelRadiusJob implements Job {
private final ExecutorService service;
private final AtomicInteger completed;
private volatile int radiusX, radiusZ;
private volatile int offsetX, offsetZ;
private volatile int total;
private final Semaphore lock;
private final int lockSize;
public ParallelRadiusJob(int concurrent) {
this(concurrent, MultiBurst.burst);
}
public ParallelRadiusJob(int concurrent, ExecutorService service) {
this.service = service;
completed = new AtomicInteger(0);
lock = new Semaphore(concurrent);
lockSize = concurrent;
}
public ParallelRadiusJob retarget(int radius, int offsetX, int offsetZ) {
return retarget(radius, radius, offsetX, offsetZ);
}
@Synchronized
public ParallelRadiusJob retarget(int radiusX, int radiusZ, int offsetX, int offsetZ) {
completed.set(0);
this.radiusX = radiusX;
this.radiusZ = radiusZ;
this.offsetX = offsetX;
this.offsetZ = offsetZ;
total = (radiusX * 2 + 1) * (radiusZ * 2 + 1);
return this;
}
@Override
@SneakyThrows
@Synchronized
public void execute() {
new Spiraler(radiusX * 2 + 3, radiusZ * 2 + 3, this::submit).drain();
lock.acquire(lockSize);
lock.release(lockSize);
}
@SneakyThrows
private void submit(int x, int z) {
if (Math.abs(x) > radiusX || Math.abs(z) > radiusZ) return;
lock.acquire();
service.submit(() -> {
try {
execute(x + offsetX, z + offsetZ);
} finally {
completeWork();
}
});
}
protected abstract void execute(int x, int z);
@Override
public void completeWork() {
completed.incrementAndGet();
lock.release();
}
@Override
public int getTotalWork() {
return total;
}
@Override
public int getWorkCompleted() {
return completed.get();
}
}

View File

@@ -37,7 +37,7 @@ public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStrea
super(); super();
this.stream = stream; this.stream = stream;
this.engine = engine; this.engine = engine;
cache = new WorldCache2D<>(stream::get); cache = new WorldCache2D<>(stream::get, size);
Iris.service(PreservationSVC.class).registerCache(this); Iris.service(PreservationSVC.class).registerCache(this);
} }
@@ -74,7 +74,7 @@ public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStrea
@Override @Override
public long getMaxSize() { public long getMaxSize() {
return 256 * 32; return cache.getMaxSize();
} }
@Override @Override

View File

@@ -0,0 +1,232 @@
package com.volmit.iris.core.pregenerator.cache
import com.volmit.iris.Iris
import com.volmit.iris.util.data.Varint
import com.volmit.iris.util.documentation.ChunkCoordinates
import com.volmit.iris.util.documentation.RegionCoordinates
import com.volmit.iris.util.io.IO
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import net.jpountz.lz4.LZ4BlockInputStream
import net.jpountz.lz4.LZ4BlockOutputStream
import java.io.*
class PregenCacheImpl(
private val directory: File,
private val maxSize: Int
) : PregenCache {
private val cache = Object2ObjectLinkedOpenHashMap<Pair<Int, Int>, Plate>()
@ChunkCoordinates
override fun isChunkCached(x: Int, z: Int): Boolean {
return this[x shr 10, z shr 10].isCached(
(x shr 5) and 31,
(z shr 5) and 31
) { isCached(x and 31, z and 31) }
}
@RegionCoordinates
override fun isRegionCached(x: Int, z: Int): Boolean {
return this[x shr 5, z shr 5].isCached(
x and 31,
z and 31,
Region::isCached
)
}
@ChunkCoordinates
override fun cacheChunk(x: Int, z: Int) {
this[x shr 10, z shr 10].cache(
(x shr 5) and 31,
(z shr 5) and 31
) { cache(x and 31, z and 31) }
}
@RegionCoordinates
override fun cacheRegion(x: Int, z: Int) {
this[x shr 5, z shr 5].cache(
x and 31,
z and 31,
Region::cache
)
}
override fun write() {
if (cache.isEmpty()) return
runBlocking {
for (plate in cache.values) {
if (!plate.dirty) continue
launch(dispatcher) {
writePlate(plate)
}
}
}
}
override fun trim(unloadDuration: Long) {
if (cache.isEmpty()) return
val threshold = System.currentTimeMillis() - unloadDuration
runBlocking {
val it = cache.values.iterator()
while (it.hasNext()) {
val plate = it.next()
if (plate.lastAccess < threshold) it.remove()
launch(dispatcher) {
writePlate(plate)
}
}
}
}
private operator fun get(x: Int, z: Int): Plate {
val key = x to z
val plate = cache.getAndMoveToFirst(key)
if (plate != null) return plate
return readPlate(x, z).also {
cache.putAndMoveToFirst(key, it)
runBlocking {
while (cache.size > maxSize) {
val plate = cache.removeLast()
launch(dispatcher) {
writePlate(plate)
}
}
}
}
}
private fun readPlate(x: Int, z: Int): Plate {
val file = fileForPlate(x, z)
if (!file.exists()) return Plate(x, z)
try {
DataInputStream(LZ4BlockInputStream(file.inputStream())).use {
return readPlate(x, z, it)
}
} catch (e: IOException) {
Iris.error("Failed to read pregen cache $file")
e.printStackTrace()
Iris.reportError(e)
}
return Plate(x, z)
}
private fun writePlate(plate: Plate) {
if (!plate.dirty) return
val file = fileForPlate(plate.x, plate.z)
try {
IO.write(file, { DataOutputStream(LZ4BlockOutputStream(it)) }, plate::write)
plate.dirty = false
} catch (e: IOException) {
Iris.error("Failed to write preen cache $file")
e.printStackTrace()
Iris.reportError(e)
}
}
private fun fileForPlate(x: Int, z: Int): File {
check(!(!directory.exists() && !directory.mkdirs())) { "Cannot create directory: " + directory.absolutePath }
return File(directory, "c.$x.$z.lz4b")
}
private class Plate(
val x: Int,
val z: Int,
private var count: Short = 0,
private var regions: Array<Region?>? = arrayOfNulls(1024)
) {
var dirty: Boolean = false
var lastAccess: Long = System.currentTimeMillis()
fun cache(x: Int, z: Int, predicate: Region.() -> Boolean): Boolean {
lastAccess = System.currentTimeMillis()
if (count == SIZE) return false
val region = regions!!.run { this[x * 32 + z] ?: Region().also { this[x * 32 + z] = it } }
if (!region.predicate()) return false
if (++count == SIZE) regions = null
dirty = true
return true
}
fun isCached(x: Int, z: Int, predicate: Region.() -> Boolean): Boolean {
lastAccess = System.currentTimeMillis()
if (count == SIZE) return true
val region = regions!![x * 32 + z] ?: return false
return region.predicate()
}
fun write(dos: DataOutput) {
Varint.writeSignedVarInt(count.toInt(), dos)
regions?.forEach {
dos.writeBoolean(it == null)
it?.write(dos)
}
}
}
private class Region(
private var count: Short = 0,
private var words: LongArray? = LongArray(64)
) {
fun cache(): Boolean {
if (count == SIZE) return false
count = SIZE
words = null
return true
}
fun cache(x: Int, z: Int): Boolean {
if (count == SIZE) return false
val words = words ?: return false
val i = x * 32 + z
val w = i shr 6
val b = 1L shl (i and 63)
val cur = (words[w] and b) != 0L
if (cur) return false
if (++count == SIZE) {
this.words = null
return true
} else {
words[w] = words[w] or b
return false
}
}
fun isCached(): Boolean = count == SIZE
fun isCached(x: Int, z: Int): Boolean {
val i = x * 32 + z
return count == SIZE || (words!![i shr 6] and (1L shl (i and 63))) != 0L
}
@Throws(IOException::class)
fun write(dos: DataOutput) {
Varint.writeSignedVarInt(count.toInt(), dos)
words?.forEach { Varint.writeUnsignedVarLong(it, dos) }
}
}
companion object {
private val dispatcher = Dispatchers.IO.limitedParallelism(4)
private const val SIZE: Short = 1024
@Throws(IOException::class)
private fun readPlate(x: Int, z: Int, din: DataInput): Plate {
val count = Varint.readSignedVarInt(din)
if (count == 1024) return Plate(x, z, SIZE, null)
return Plate(x, z, count.toShort(), Array(1024) {
if (din.readBoolean()) null
else readRegion(din)
})
}
@Throws(IOException::class)
private fun readRegion(din: DataInput): Region {
val count = Varint.readSignedVarInt(din)
return if (count == 1024) Region(SIZE, null)
else Region(count.toShort(), LongArray(64) { Varint.readUnsignedVarLong(din) })
}
}
}

View File

@@ -14,9 +14,9 @@ abstract class EngineScript
object EngineScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), { object EngineScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
providedProperties( providedProperties(
"engine" to Engine::class, "engine" to Engine::class,
"complex" to IrisComplex::class,
"seed" to Long::class, "seed" to Long::class,
"dimension" to IrisDimension::class, "dimension" to IrisDimension::class,
"complex" to IrisComplex::class,
"biome" to BiomeLookup::class, "biome" to BiomeLookup::class,
) )
}) { }) {

View File

@@ -1,6 +1,8 @@
package com.volmit.iris.core.scripting.kotlin.base package com.volmit.iris.core.scripting.kotlin.base
import com.volmit.iris.core.loader.IrisRegistrant import com.volmit.iris.core.loader.IrisRegistrant
import com.volmit.iris.engine.framework.Engine
import com.volmit.iris.engine.`object`.IrisDimension
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ScriptCompilationConfiguration import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.providedProperties import kotlin.script.experimental.api.providedProperties
@@ -8,8 +10,13 @@ import kotlin.script.experimental.api.providedProperties
@KotlinScript(fileExtension = "proc.kts", compilationConfiguration = PreprocessorScriptDefinition::class) @KotlinScript(fileExtension = "proc.kts", compilationConfiguration = PreprocessorScriptDefinition::class)
abstract class PreprocessorScript abstract class PreprocessorScript
object PreprocessorScriptDefinition : ScriptCompilationConfiguration(listOf(EngineScriptDefinition), { object PreprocessorScriptDefinition : ScriptCompilationConfiguration(listOf(DataScriptDefinition), {
providedProperties("object" to IrisRegistrant::class) providedProperties(
"engine" to Engine::class,
"seed" to Long::class,
"dimension" to IrisDimension::class,
"object" to IrisRegistrant::class
)
}) { }) {
private fun readResolve(): Any = PreprocessorScriptDefinition private fun readResolve(): Any = PreprocessorScriptDefinition
} }

View File

@@ -9,15 +9,20 @@ import com.volmit.iris.core.scripting.kotlin.base.EngineScript
import com.volmit.iris.core.scripting.kotlin.base.MobSpawningScript import com.volmit.iris.core.scripting.kotlin.base.MobSpawningScript
import com.volmit.iris.core.scripting.kotlin.base.PostMobSpawningScript import com.volmit.iris.core.scripting.kotlin.base.PostMobSpawningScript
import com.volmit.iris.core.scripting.kotlin.base.PreprocessorScript import com.volmit.iris.core.scripting.kotlin.base.PreprocessorScript
import com.volmit.iris.core.scripting.kotlin.environment.IrisSimpleExecutionEnvironment
import com.volmit.iris.core.scripting.kotlin.runner.ScriptRunner
import com.volmit.iris.engine.framework.Engine import com.volmit.iris.engine.framework.Engine
import com.volmit.iris.util.mantle.MantleChunk import com.volmit.iris.util.mantle.MantleChunk
import org.bukkit.Chunk import org.bukkit.Chunk
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.entity.Entity import org.bukkit.entity.Entity
import java.io.File
data class IrisExecutionEnvironment( class IrisExecutionEnvironment internal constructor(
private val engine: Engine private val engine: Engine,
) : IrisPackExecutionEnvironment(engine.data), EngineEnvironment { parent: ScriptRunner?,
) : IrisPackExecutionEnvironment(engine.data, parent), EngineEnvironment {
constructor(engine: Engine) : this(engine, null)
override fun getEngine() = engine override fun getEngine() = engine
override fun execute(script: String) = override fun execute(script: String) =
@@ -33,18 +38,24 @@ data class IrisExecutionEnvironment(
execute(script, PostMobSpawningScript::class.java, engine.parameters("location" to location, "entity" to mob)) execute(script, PostMobSpawningScript::class.java, engine.parameters("location" to location, "entity" to mob))
override fun preprocessObject(script: String, `object`: IrisRegistrant) = override fun preprocessObject(script: String, `object`: IrisRegistrant) =
execute(script, PreprocessorScript::class.java, engine.parameters("object" to `object`)) execute(script, PreprocessorScript::class.java, engine.limitedParameters("object" to `object`))
override fun updateChunk(script: String, mantleChunk: MantleChunk, chunk: Chunk, executor: UpdateExecutor) = override fun updateChunk(script: String, mantleChunk: MantleChunk, chunk: Chunk, executor: UpdateExecutor) =
execute(script, ChunkUpdateScript::class.java, engine.parameters("mantleChunk" to mantleChunk, "chunk" to chunk, "executor" to executor)) execute(script, ChunkUpdateScript::class.java, engine.parameters("mantleChunk" to mantleChunk, "chunk" to chunk, "executor" to executor))
private fun Engine.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> { private fun Engine.limitedParameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
return mapOf( return mapOf(
"data" to data, "data" to data,
"engine" to this, "engine" to this,
"complex" to complex,
"seed" to seedManager.seed, "seed" to seedManager.seed,
"dimension" to dimension, "dimension" to dimension,
*values,
)
}
private fun Engine.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
return limitedParameters(
"complex" to complex,
"biome" to BiomeLookup(::getSurfaceBiome), "biome" to BiomeLookup(::getSurfaceBiome),
*values, *values,
) )

View File

@@ -1,17 +1,22 @@
package com.volmit.iris.core.scripting.kotlin.environment package com.volmit.iris.core.scripting.kotlin.environment
import com.volmit.iris.core.loader.IrisData import com.volmit.iris.core.loader.IrisData
import com.volmit.iris.core.scripting.environment.EngineEnvironment
import com.volmit.iris.core.scripting.environment.PackEnvironment import com.volmit.iris.core.scripting.environment.PackEnvironment
import com.volmit.iris.core.scripting.kotlin.base.DataScript import com.volmit.iris.core.scripting.kotlin.base.DataScript
import com.volmit.iris.core.scripting.kotlin.base.NoiseScript import com.volmit.iris.core.scripting.kotlin.base.NoiseScript
import com.volmit.iris.core.scripting.kotlin.runner.Script import com.volmit.iris.core.scripting.kotlin.runner.Script
import com.volmit.iris.core.scripting.kotlin.runner.ScriptRunner
import com.volmit.iris.core.scripting.kotlin.runner.valueOrThrow import com.volmit.iris.core.scripting.kotlin.runner.valueOrThrow
import com.volmit.iris.engine.framework.Engine
import com.volmit.iris.util.math.RNG import com.volmit.iris.util.math.RNG
import kotlin.reflect.KClass import kotlin.reflect.KClass
open class IrisPackExecutionEnvironment( open class IrisPackExecutionEnvironment internal constructor(
private val data: IrisData private val data: IrisData,
) : IrisSimpleExecutionEnvironment(data.dataFolder), PackEnvironment { parent: ScriptRunner?
) : IrisSimpleExecutionEnvironment(data.dataFolder, parent), PackEnvironment {
constructor(data: IrisData) : this(data, null)
override fun getData() = data override fun getData() = data
@@ -31,6 +36,9 @@ open class IrisPackExecutionEnvironment(
override fun createNoise(script: String, rng: RNG) = override fun createNoise(script: String, rng: RNG) =
evaluate(script, NoiseScript::class.java, data.parameters("rng" to rng)) evaluate(script, NoiseScript::class.java, data.parameters("rng" to rng))
override fun with(engine: Engine) =
IrisExecutionEnvironment(engine, runner)
private fun IrisData.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> { private fun IrisData.parameters(vararg values: Pair<String, Any?>): Map<String, Any?> {
return mapOf( return mapOf(
"data" to this, "data" to this,

View File

@@ -4,10 +4,11 @@ import com.volmit.iris.Iris
import com.volmit.iris.core.IrisSettings import com.volmit.iris.core.IrisSettings
import com.volmit.iris.core.scripting.environment.SimpleEnvironment import com.volmit.iris.core.scripting.environment.SimpleEnvironment
import com.volmit.iris.core.scripting.kotlin.base.* import com.volmit.iris.core.scripting.kotlin.base.*
import com.volmit.iris.core.scripting.kotlin.runner.FileComponents
import com.volmit.iris.core.scripting.kotlin.runner.Script import com.volmit.iris.core.scripting.kotlin.runner.Script
import com.volmit.iris.core.scripting.kotlin.runner.ScriptRunner import com.volmit.iris.core.scripting.kotlin.runner.ScriptRunner
import com.volmit.iris.core.scripting.kotlin.runner.classpath import com.volmit.iris.core.scripting.kotlin.runner.classpath
import com.volmit.iris.core.scripting.kotlin.runner.valueOrNull import com.volmit.iris.core.scripting.kotlin.runner.value
import com.volmit.iris.core.scripting.kotlin.runner.valueOrThrow import com.volmit.iris.core.scripting.kotlin.runner.valueOrThrow
import com.volmit.iris.util.collection.KMap import com.volmit.iris.util.collection.KMap
import com.volmit.iris.util.data.KCache import com.volmit.iris.util.data.KCache
@@ -18,11 +19,14 @@ import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ResultWithDiagnostics import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.text.split import kotlin.text.split
open class IrisSimpleExecutionEnvironment( open class IrisSimpleExecutionEnvironment internal constructor(
baseDir: File = File(".").absoluteFile baseDir: File,
parent: ScriptRunner?
) : SimpleEnvironment { ) : SimpleEnvironment {
protected val compileCache = KCache<String, KMap<KClass<*>, ResultWithDiagnostics<Script>>>({ _ -> KMap() }, IrisSettings.get().performance.cacheSize.toLong()) @JvmOverloads
protected val runner = ScriptRunner(baseDir) constructor(baseDir: File = File(".").absoluteFile) : this(baseDir, null)
protected val compileCache = KCache<String, KMap<KClass<*>, ResultWithDiagnostics<Script>>>({ _ -> KMap() }, 1024L)
protected val runner = ScriptRunner(baseDir, parent)
override fun execute( override fun execute(
script: String script: String
@@ -50,11 +54,6 @@ open class IrisSimpleExecutionEnvironment(
return evaluate0(script, type.kotlin, vars) return evaluate0(script, type.kotlin, vars)
} }
override fun close() {
compileCache.invalidate()
runner.clear()
}
protected open fun compile(script: String, type: KClass<*>) = protected open fun compile(script: String, type: KClass<*>) =
compileCache.get(script) compileCache.get(script)
.computeIfAbsent(type) { _ -> runner.compile(type, script) } .computeIfAbsent(type) { _ -> runner.compile(type, script) }
@@ -68,7 +67,7 @@ open class IrisSimpleExecutionEnvironment(
return compile(name, type) return compile(name, type)
.evaluate(properties) .evaluate(properties)
.valueOrThrow("Failed to evaluate script") .valueOrThrow("Failed to evaluate script")
.valueOrNull() .value()
} catch (e: Throwable) { } catch (e: Throwable) {
e.printStackTrace() e.printStackTrace()
} }
@@ -89,7 +88,7 @@ open class IrisSimpleExecutionEnvironment(
} }
companion object { companion object {
private const val CLASSPATH = "val classpath = files(" private const val CLASSPATH = "val classpath = mapOf("
private fun File.updateClasspath(classpath: List<File>) { private fun File.updateClasspath(classpath: List<File>) {
val test = if (exists()) readLines() else BASE_GRADLE val test = if (exists()) readLines() else BASE_GRADLE
@@ -97,24 +96,71 @@ open class IrisSimpleExecutionEnvironment(
} }
private fun List<String>.updateClasspath(classpath: List<File>): String { private fun List<String>.updateClasspath(classpath: List<File>): String {
val classpath = classpath.joinToString(",", CLASSPATH, ")") { "\"${it.escapedPath}\"" } val components = linkedMapOf<String, FileComponents>()
val index = indexOfFirst { it.startsWith(CLASSPATH) } classpath.forEach {
if (index == -1) { val parts = it.canonicalPath.split(File.separatorChar)
return "$classpath\n${joinToString("\n")}" if (parts.size <= 1) {
Iris.error("Invalid classpath entry: $it")
return@forEach
} }
var parent = components.computeIfAbsent(parts[0]) { FileComponents(parts[0], true) }
for (part in parts.subList(1, parts.size)) {
parent = parent.append(part)
}
}
val mapped = components.values.associate {
var current = it
val root = buildString {
while (current.children.size == 1) {
append(current.segment)
append(File.separatorChar)
current = current.children.first()
}
append(current.segment)
append(File.separatorChar)
}.escapedPath
val result = mutableSetOf<String>()
val queue = ArrayDeque<Pair<String?, Collection<FileComponents>>>()
queue.add(null to current.children)
while (queue.isNotEmpty()) {
val pair = queue.removeFirst()
val path = pair.first?.let { p -> p + File.separatorChar } ?: ""
pair.second.forEach { child ->
val path = path + child.segment
if (child.children.isEmpty()) result.add(path.escapedPath)
else queue.add(path to child.children)
}
}
root to result
}
val classpath = mapped.entries.joinToString(",", CLASSPATH, ")") {
"\"${it.key}\" to setOf(${it.value.joinToString(", ") { f -> "\"$f\"" }})"
}
val mod = toMutableList() val mod = toMutableList()
mod[index] = classpath val index = indexOfFirst { it.startsWith(CLASSPATH) }
if (index == -1) {
mod.clear()
mod.addAll(BASE_GRADLE)
}
mod[if (index == -1) 0 else index] = classpath
return mod.joinToString("\n") return mod.joinToString("\n")
} }
private val File.escapedPath private val String.escapedPath
get() = absolutePath.replace("\\", "\\\\").replace("\"", "\\\"") get() = replace("\\", "\\\\").replace("\"", "\\\"")
private const val ARTIFACT_ID = $$"local:${it.relativeTo(home).path.substringBeforeLast(\".jar\")}:1.0.0" private const val ARTIFACT_ID = $$"local:${it.substringBeforeLast(\".jar\")}:1.0.0"
private val BASE_GRADLE = """ private val BASE_GRADLE = """
val classpath = files() val classpath = mapOf()
val home = file(System.getProperty("user.home"))
plugins { plugins {
kotlin("jvm") version("2.2.0") kotlin("jvm") version("2.2.0")
@@ -123,7 +169,7 @@ open class IrisSimpleExecutionEnvironment(
repositories { repositories {
mavenCentral() mavenCentral()
flatDir { flatDir {
dirs(home) dirs(classpath.keys)
} }
} }
@@ -135,7 +181,7 @@ open class IrisSimpleExecutionEnvironment(
configurations.kotlinCompilerPluginClasspath { extendsFrom(script) } configurations.kotlinCompilerPluginClasspath { extendsFrom(script) }
dependencies { dependencies {
classpath.forEach { script("$ARTIFACT_ID") } classpath.values.flatMap { it }.forEach { script("$ARTIFACT_ID") }
}""".trimIndent().split("\n") }""".trimIndent().split("\n")
} }
} }

View File

@@ -19,24 +19,18 @@ import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
class ScriptRunner( class ScriptRunner(
private val host: BasicJvmScriptingHost, val baseDir: File,
val baseDir: File parent: ScriptRunner? = null,
private val host: BasicJvmScriptingHost = BasicJvmScriptingHost()
) { ) {
constructor(baseDir: File) : this(BasicJvmScriptingHost(), baseDir)
private val configs = ConcurrentHashMap<KClass<*>, ScriptCompilationConfiguration>() private val configs = ConcurrentHashMap<KClass<*>, ScriptCompilationConfiguration>()
private val hostConfig = host.baseHostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration) private val hostConfig = host.baseHostConfiguration.withDefaultsFrom(defaultJvmScriptingHostConfiguration)
private val sharedClassLoader = SharedClassLoader() private val sharedClassLoader: SharedClassLoader = parent?.sharedClassLoader ?: SharedClassLoader()
private var resolver = createResolver(baseDir) private val resolver = createResolver(baseDir)
fun compile(type: KClass<*>, raw: String, name: String? = null) = compile(type, raw.toScriptSource(name)) fun compile(type: KClass<*>, raw: String, name: String? = null) = compile(type, raw.toScriptSource(name))
fun compile(type: KClass<*>, file: File, preloaded: String? = null) = compile(type, FileScriptSource(file, preloaded)) fun compile(type: KClass<*>, file: File, preloaded: String? = null) = compile(type, FileScriptSource(file, preloaded))
fun clear() {
configs.clear()
resolver = createResolver(baseDir)
}
private fun compile( private fun compile(
type: KClass<*>, type: KClass<*>,
code: SourceCode code: SourceCode
@@ -53,6 +47,7 @@ class ScriptRunner(
dependencyResolver(resolver) dependencyResolver(resolver)
packDirectory(baseDir) packDirectory(baseDir)
sharedClassloader(sharedClassLoader) sharedClassloader(sharedClassLoader)
server(true)
if (SimpleScript::class.java.isAssignableFrom(type.java)) if (SimpleScript::class.java.isAssignableFrom(type.java))
return@createCompilationConfigurationFromTemplate return@createCompilationConfigurationFromTemplate

View File

@@ -12,6 +12,7 @@ import kotlin.script.experimental.dependencies.addRepository
import kotlin.script.experimental.dependencies.impl.SimpleExternalDependenciesResolverOptionsParser import kotlin.script.experimental.dependencies.impl.SimpleExternalDependenciesResolverOptionsParser
import kotlin.script.experimental.jvm.JvmDependency import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.JvmDependencyFromClassLoader import kotlin.script.experimental.jvm.JvmDependencyFromClassLoader
import kotlin.script.experimental.jvm.updateClasspath
import kotlin.script.experimental.jvm.util.classpathFromClassloader import kotlin.script.experimental.jvm.util.classpathFromClassloader
import kotlin.script.experimental.util.PropertiesCollection import kotlin.script.experimental.util.PropertiesCollection
import kotlin.script.experimental.util.filterByAnnotationType import kotlin.script.experimental.util.filterByAnnotationType
@@ -21,13 +22,41 @@ internal fun <T, R> ResultWithDiagnostics<T>.map(transformer: (T) -> R): ResultW
is ResultWithDiagnostics.Failure -> this is ResultWithDiagnostics.Failure -> this
} }
internal fun EvaluationResult.valueOrNull() = returnValue.valueOrNull() internal fun EvaluationResult.value() = returnValue.value()
internal fun ResultValue.valueOrNull(): Any? = internal fun ResultValue.value(): Any? =
when (this) { when (this) {
is ResultValue.Value -> value is ResultValue.Value -> value
is ResultValue.Error -> throw error
else -> null else -> null
} }
internal class FileComponents(
val segment: String,
val root: Boolean = false,
) {
private val children0 = mutableMapOf<String, FileComponents>()
val children get() = children0.values
fun append(segment: String): FileComponents =
children0.computeIfAbsent(segment) { FileComponents(segment) }
override fun hashCode(): Int {
var result = segment.hashCode()
result = 31 * result + children0.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FileComponents) return false
if (segment != other.segment) return false
if (children0 != other.children0) return false
return true
}
}
private val workDir = File(".").normalize() private val workDir = File(".").normalize()
internal fun createResolver(baseDir: File = workDir) = CompoundDependenciesResolver(baseDir) internal fun createResolver(baseDir: File = workDir) = CompoundDependenciesResolver(baseDir)
@@ -39,8 +68,9 @@ private fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefineme
?: return context.compilationConfiguration.asSuccess() ?: return context.compilationConfiguration.asSuccess()
val reports = mutableListOf<ScriptDiagnostic>() val reports = mutableListOf<ScriptDiagnostic>()
val loader = context.compilationConfiguration[ScriptCompilationConfiguration.sharedClassloader] ?: loader val loader = context.compilationConfiguration[ScriptCompilationConfiguration.sharedClassloader]
val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver val resolver = context.compilationConfiguration[ScriptCompilationConfiguration.dependencyResolver] ?: resolver
val server = context.compilationConfiguration[ScriptCompilationConfiguration.server] ?: false
context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory] context.compilationConfiguration[ScriptCompilationConfiguration.packDirectory]
?.addPack(resolver) ?.addPack(resolver)
?: context.script.locationId ?: context.script.locationId
@@ -65,13 +95,18 @@ private fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefineme
} }
return runBlocking { return runBlocking {
resolver.resolveDependencies(annotations) resolver.resolveDependencies(annotations, server)
}.onSuccess { classpath -> }.onSuccess { classpath ->
context.compilationConfiguration.with { context.compilationConfiguration.with {
if (!server) {
updateClasspath(classpath.map { it.first })
return@with
}
val newClasspath = classpath.filterNewClasspath(this[ScriptCompilationConfiguration.dependencies]) val newClasspath = classpath.filterNewClasspath(this[ScriptCompilationConfiguration.dependencies])
?: return@with ?: return@with
val shared = classpath.mapNotNull { p -> p.first.takeIf { p.second } } val shared = classpath.mapNotNull { p -> p.first.takeIf { p.second } }
if (shared.isNotEmpty()) loader.addFiles(shared) if (shared.isNotEmpty()) loader!!.addFiles(shared)
val regular = newClasspath val regular = newClasspath
.map { p -> p.first } .map { p -> p.first }
@@ -92,7 +127,8 @@ private fun Collection<Pair<File, Boolean>>.filterNewClasspath(known: Collection
} }
private suspend fun ExternalDependenciesResolver.resolveDependencies( private suspend fun ExternalDependenciesResolver.resolveDependencies(
annotations: Iterable<ScriptSourceAnnotation<*>> annotations: Iterable<ScriptSourceAnnotation<*>>,
server: Boolean
): ResultWithDiagnostics<List<Pair<File, Boolean>>> { ): ResultWithDiagnostics<List<Pair<File, Boolean>>> {
val reports = mutableListOf<ScriptDiagnostic>() val reports = mutableListOf<ScriptDiagnostic>()
annotations.forEach { (annotation, locationWithId) -> annotations.forEach { (annotation, locationWithId) ->
@@ -124,6 +160,10 @@ private suspend fun ExternalDependenciesResolver.resolveDependencies(
*annotation.options, *annotation.options,
locationWithId = locationWithId locationWithId = locationWithId
).onSuccess { options -> ).onSuccess { options ->
if (!server && true == options.server) {
return@onSuccess listOf<Pair<File, Boolean>>().asSuccess()
}
annotation.artifactsCoordinates.asIterable().flatMapSuccess { artifactCoordinates -> annotation.artifactsCoordinates.asIterable().flatMapSuccess { artifactCoordinates ->
resolve(artifactCoordinates, options, locationWithId) resolve(artifactCoordinates, options, locationWithId)
}.map { files -> files.map { it to (options.shared ?: false) } } }.map { files -> files.map { it to (options.shared ?: false) } }
@@ -132,6 +172,7 @@ private suspend fun ExternalDependenciesResolver.resolveDependencies(
} }
private val ExternalDependenciesResolver.Options.shared get() = flag("shared") private val ExternalDependenciesResolver.Options.shared get() = flag("shared")
private val ExternalDependenciesResolver.Options.server get() = flag("server")
internal val ClassLoader.classpath get() = classpathFromClassloader(this) ?: emptyList() internal val ClassLoader.classpath get() = classpathFromClassloader(this) ?: emptyList()
internal fun <R> ResultWithDiagnostics<R>.valueOrThrow(message: CharSequence): R = valueOr { internal fun <R> ResultWithDiagnostics<R>.valueOrThrow(message: CharSequence): R = valueOr {
@@ -139,8 +180,9 @@ internal fun <R> ResultWithDiagnostics<R>.valueOrThrow(message: CharSequence): R
} }
internal val ScriptCompilationConfigurationKeys.dependencyResolver by PropertiesCollection.key(resolver, true) internal val ScriptCompilationConfigurationKeys.dependencyResolver by PropertiesCollection.key(resolver, true)
internal val ScriptCompilationConfigurationKeys.packDirectory by PropertiesCollection.key(workDir, true) internal val ScriptCompilationConfigurationKeys.packDirectory by PropertiesCollection.key<File>(null, true)
internal val ScriptCompilationConfigurationKeys.sharedClassloader by PropertiesCollection.key(loader, true) internal val ScriptCompilationConfigurationKeys.sharedClassloader by PropertiesCollection.key<SharedClassLoader>(null, true)
internal val ScriptCompilationConfigurationKeys.server by PropertiesCollection.key(false, isTransient = true)
private fun File.addPack(resolver: CompoundDependenciesResolver) = resolver.addPack(this) private fun File.addPack(resolver: CompoundDependenciesResolver) = resolver.addPack(this)
private fun <R> ResultWithDiagnostics<R>.appendReports(reports : Collection<ScriptDiagnostic>) = private fun <R> ResultWithDiagnostics<R>.appendReports(reports : Collection<ScriptDiagnostic>) =
@@ -162,7 +204,9 @@ internal fun ScriptCompilationConfiguration.Builder.configure() {
refineConfiguration { refineConfiguration {
beforeParsing { context -> try { beforeParsing { context -> try {
context.compilationConfiguration.with { context.compilationConfiguration.with {
ScriptCompilationConfiguration.dependencies.append((this[ScriptCompilationConfiguration.sharedClassloader] ?: loader).dependency) if (context.compilationConfiguration[ScriptCompilationConfiguration.server] ?: false) {
ScriptCompilationConfiguration.dependencies.append(this[ScriptCompilationConfiguration.sharedClassloader]!!.dependency)
}
}.asSuccess() }.asSuccess()
} catch (e: Throwable) { } catch (e: Throwable) {
ResultWithDiagnostics.Failure(e.asDiagnostics()) ResultWithDiagnostics.Failure(e.asDiagnostics())

View File

@@ -0,0 +1,77 @@
package com.volmit.iris.engine.mantle
import com.volmit.iris.core.IrisSettings
import com.volmit.iris.core.nms.container.Pair
import com.volmit.iris.engine.framework.Engine
import com.volmit.iris.util.context.ChunkContext
import com.volmit.iris.util.documentation.ChunkCoordinates
import com.volmit.iris.util.mantle.Mantle
import com.volmit.iris.util.mantle.MantleChunk
import com.volmit.iris.util.mantle.flag.MantleFlag
import com.volmit.iris.util.parallel.MultiBurst
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext
interface MatterGenerator {
val engine: Engine
val mantle: Mantle
val radius: Int
val realRadius: Int
val components: List<Pair<List<MantleComponent>, Int>>
@ChunkCoordinates
fun generateMatter(x: Int, z: Int, multicore: Boolean, context: ChunkContext) {
if (!engine.dimension.isUseMantle || mantle.hasFlag(x, z, MantleFlag.PLANNED))
return
val multicore = multicore || IrisSettings.get().generator.isUseMulticoreMantle
mantle.write(engine.mantle, x, z, radius, multicore).use { writer ->
for (pair in components) {
radius(x, z, pair.b, { x, z ->
for (c in pair.a) {
emit(Triple(x, z, c))
}
}, { (x, z, c) -> launch(multicore) {
acquireChunk(multicore, writer, x, z)
.raiseFlagSuspend(MantleFlag.PLANNED, c.flag) {
c.generateLayer(writer, x, z, context)
}
}})
}
radius(x, z, realRadius, { x, z ->
emit(Pair(x, z))
}, {
writer.acquireChunk(it.a, it.b)
.flag(MantleFlag.PLANNED, true)
})
}
}
private fun <T> radius(x: Int, z: Int, radius: Int, collector: suspend FlowCollector<T>.(Int, Int) -> Unit, task: suspend CoroutineScope.(T) -> Unit) = runBlocking {
flow {
for (i in -radius..radius) {
for (j in -radius..radius) {
collector(x + i, z + j)
}
}
}.collect { task(it) }
}
companion object {
private val dispatcher = MultiBurst.burst.dispatcher
private fun CoroutineScope.launch(multicore: Boolean, block: suspend CoroutineScope.() -> Unit) =
launch(if (multicore) dispatcher else EmptyCoroutineContext, block = block)
private suspend fun CoroutineScope.acquireChunk(multicore: Boolean, writer: MantleWriter, x: Int, z: Int): MantleChunk {
return if (multicore) async(Dispatchers.IO) { writer.acquireChunk(x, z) }.await()
else writer.acquireChunk(x, z)
}
}
}

View File

@@ -0,0 +1,35 @@
package com.volmit.iris.util.context
import com.volmit.iris.engine.IrisComplex
import com.volmit.iris.engine.`object`.IrisBiome
import com.volmit.iris.engine.`object`.IrisRegion
import com.volmit.iris.util.parallel.MultiBurst
import kotlinx.coroutines.*
import org.bukkit.block.data.BlockData
class ChunkContext @JvmOverloads constructor(
val x: Int,
val z: Int,
c: IrisComplex,
cache: Boolean = true,
) {
val height: ChunkedDataCache<Double> = ChunkedDataCache(c.heightStream, x, z, cache)
val biome: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.trueBiomeStream, x, z, cache)
val cave: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.caveBiomeStream, x, z, cache)
val rock: ChunkedDataCache<BlockData> = ChunkedDataCache(c.rockStream, x, z, cache)
val fluid: ChunkedDataCache<BlockData> = ChunkedDataCache(c.fluidStream, x, z, cache)
val region: ChunkedDataCache<IrisRegion> = ChunkedDataCache(c.regionStream, x, z, cache)
init {
if (cache) runBlocking {
val dispatcher = MultiBurst.burst.dispatcher
launch { height.fill(dispatcher) }
launch { biome.fill(dispatcher) }
launch { cave.fill(dispatcher) }
launch { rock.fill(dispatcher) }
launch { fluid.fill(dispatcher) }
launch { region.fill(dispatcher) }
}
}
}

View File

@@ -0,0 +1,46 @@
package com.volmit.iris.util.context
import com.volmit.iris.util.documentation.BlockCoordinates
import com.volmit.iris.util.stream.ProceduralStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlin.coroutines.CoroutineContext
class ChunkedDataCache<T> private constructor(
private val x: Int,
private val z: Int,
private val stream: ProceduralStream<T?>,
private val cache: Boolean
) {
private val data = arrayOfNulls<Any>(if (cache) 256 else 0)
@JvmOverloads
@BlockCoordinates
constructor(stream: ProceduralStream<T?>, x: Int, z: Int, cache: Boolean = true) : this(x, z, stream, cache)
suspend fun fill(context: CoroutineContext = Dispatchers.Default) {
if (!cache) return
supervisorScope {
for (i in 0 until 16) {
for (j in 0 until 16) {
launch(context) {
val t = stream.get((x + i).toDouble(), (z + j).toDouble())
data[(j * 16) + i] = t
}
}
}
}
}
@BlockCoordinates
fun get(x: Int, z: Int): T? {
if (!cache) {
return stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
}
val t = data[(z * 16) + x] as? T
return t ?: stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
}
}

View File

@@ -0,0 +1,102 @@
package com.volmit.iris.util.mantle
import com.volmit.iris.util.data.Varint
import com.volmit.iris.util.mantle.flag.MantleFlag
import com.volmit.iris.util.parallel.AtomicBooleanArray
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.DataInput
import java.io.DataOutput
import java.io.IOException
import java.lang.Byte
import kotlin.Boolean
import kotlin.IllegalStateException
import kotlin.Int
import kotlin.Throwable
import kotlin.Throws
import kotlin.Unit
abstract class FlaggedChunk() {
private val flags = AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1)
private val locks = 0.rangeUntil(flags.length()).map { Mutex() }.toTypedArray()
abstract fun isClosed(): Boolean
protected fun copyFlags(other: FlaggedChunk) {
for (i in 0 until flags.length()) {
flags.set(i, other.flags.get(i))
}
}
fun isFlagged(flag: MantleFlag) = flags.get(flag.ordinal())
fun flag(flag: MantleFlag, value: Boolean) {
if (isClosed()) throw IllegalStateException("Chunk is closed!")
flags.set(flag.ordinal(), value)
}
suspend fun raiseFlagSuspend(guard: MantleFlag?, flag: MantleFlag, task: suspend () -> Unit) {
if (isClosed()) throw IllegalStateException("Chunk is closed!")
if (guard != null && isFlagged(guard)) return
locks[flag.ordinal()].withLock {
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
task()
} catch (e: Throwable) {
flags.set(flag.ordinal(), false)
throw e
}
}
}
}
fun raiseFlagUnchecked(flag: MantleFlag, task: Runnable) {
if (isClosed()) throw IllegalStateException("Chunk is closed!")
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
task.run()
} catch (e: Throwable) {
flags.set(flag.ordinal(), false)
throw e
}
}
}
@Throws(IOException::class)
protected fun readFlags(version: Int, din: DataInput) {
val l = if (version < 0) 16 else Varint.readUnsignedVarInt(din)
if (version >= 1) {
var i = 0
while (i < l) {
val f = din.readByte()
var j = 0
while (j < Byte.SIZE && i < flags.length()) {
flags.set(i, (f.toInt() and (1 shl j)) != 0)
j++
i++
}
}
} else {
for (i in 0 until l) {
flags.set(i, din.readBoolean())
}
}
}
@Throws(IOException::class)
protected fun writeFlags(dos: DataOutput) {
Varint.writeUnsignedVarInt(flags.length(), dos)
val count = flags.length()
var i = 0
while (i < count) {
var f = 0
for (j in 0 until Byte.SIZE) {
if (i >= count) break
f = f or if (flags.get(i)) 1 shl j else 0
i++
}
dos.write(f)
}
}
}

View File

@@ -21,6 +21,5 @@ org.gradle.jvmargs=-Xmx3072m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF
org.gradle.caching=true org.gradle.caching=true
org.gradle.configureondemand=false org.gradle.configureondemand=false
nmsTools.useBuildTools=false
nmsTools.repo-url=https://repo.codemc.org/repository/nms/ nmsTools.repo-url=https://repo.codemc.org/repository/nms/
nmsTools.specialSourceVersion=1.11.4 nmsTools.specialSourceVersion=1.11.4

View File

@@ -168,13 +168,13 @@ public class NMSBinding implements INMSBinding {
@Override @Override
public boolean hasTile(Location l) { public boolean hasTile(Location l) {
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null; return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())) != null;
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public KMap<String, Object> serializeTile(Location location) { public KMap<String, Object> serializeTile(Location location) {
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false); BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
if (e == null) { if (e == null) {
return null; return null;

View File

@@ -0,0 +1,169 @@
package com.volmit.iris.core.nms.v1_21_R6;
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_21_R6.CraftServer;
import org.bukkit.craftbukkit.v1_21_R6.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<Biome> biomeCustomRegistry;
private final Registry<Biome> biomeRegistry;
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final RNG rng;
private final KMap<String, Holder<Biome>> customBiomes;
public CustomBiomeSource(long seed, Engine engine, World world) {
this.engine = engine;
this.seed = seed;
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
this.rng = new RNG(engine.getSeedManager().getBiome());
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
}
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
List<Holder<Biome>> b = new ArrayList<>();
for (IrisBiome i : engine.getAllBiomes()) {
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry
.getValue(ResourceLocation.fromNamespaceAndPath(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> T fieldForClass(Class<T> 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<Holder<Biome>> collectPossibleBiomes() {
return getAllBiomes(
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
.lookup(Registries.BIOME).orElse(null),
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
engine).stream();
}
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
KMap<String, Holder<Biome>> m = new KMap<>();
for (IrisBiome i : engine.getAllBiomes()) {
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId());
Biome biome = customRegistry.getValue(resourceLocation);
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
if (optionalBiomeKey.isEmpty()) {
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
continue;
}
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(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<? extends BiomeSource> codec() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public Holder<Biome> 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);
}
}
}

View File

@@ -0,0 +1,449 @@
package com.volmit.iris.core.nms.v1_21_R6;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.MapCodec;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.ResultLocator;
import com.volmit.iris.engine.framework.WrongEngineBroException;
import com.volmit.iris.engine.object.IrisJigsawStructure;
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
import com.volmit.iris.engine.object.IrisStructurePopulator;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.reflect.WrappedField;
import com.volmit.iris.util.reflect.WrappedReturningMethod;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.*;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedList;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.*;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.levelgen.*;
import net.minecraft.world.level.levelgen.blending.Blender;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_21_R6.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R6.generator.CustomChunkGenerator;
import org.bukkit.craftbukkit.v1_21_R6.generator.structure.CraftStructure;
import org.bukkit.event.world.AsyncStructureSpawnEvent;
import org.spigotmc.SpigotWorldConfig;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
public class IrisChunkGenerator extends CustomChunkGenerator {
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
private final ChunkGenerator delegate;
private final Engine engine;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
private final IrisStructurePopulator populator;
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
this.delegate = delegate;
this.engine = engine;
this.populator = new IrisStructurePopulator(engine);
var dimension = engine.getDimension();
KSet<IrisJigsawStructure> placements = new KSet<>();
addAll(dimension.getJigsawStructures(), placements);
for (var region : dimension.getAllRegions(engine)) {
addAll(region.getJigsawStructures(), placements);
for (var biome : region.getAllBiomes(engine))
addAll(biome.getJigsawStructures(), placements);
}
var stronghold = dimension.getStronghold();
if (stronghold != null)
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
placements.removeIf(Objects::isNull);
var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow();
for (var s : placements) {
try {
String raw = s.getStructureKey();
if (raw == null) continue;
boolean tag = raw.startsWith("#");
if (tag) raw = raw.substring(1);
var location = ResourceLocation.parse(raw);
if (!tag) {
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
continue;
}
var key = TagKey.create(Registries.STRUCTURE, location);
var set = registry.get(key).orElse(null);
if (set == null) {
Iris.error("Could not find structure tag: " + raw);
continue;
}
for (var holder : set) {
var resourceKey = holder.unwrapKey().orElse(null);
if (resourceKey == null) continue;
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
}
} catch (Throwable e) {
Iris.error("Failed to load structure: " + s.getLoadKey());
e.printStackTrace();
}
}
}
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
if (placements == null) return;
placements.stream()
.map(IrisJigsawStructurePlacement::getStructure)
.map(engine.getData().getJigsawStructureLoader()::load)
.filter(Objects::nonNull)
.forEach(structures::add);
}
@Override
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
if (holders.size() == 0) return null;
if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
}
if (engine.getDimension().isDisableExplorerMaps())
return null;
KMap<String, Holder<Structure>> structures = new KMap<>();
for (var holder : holders) {
if (holder == null) continue;
var key = holder.unwrapKey().orElse(null);
var set = this.structures.get(key);
if (set == null) continue;
for (var structure : set) {
structures.put(structure, holder);
}
}
if (structures.isEmpty())
return null;
var locator = ResultLocator.locateStructure(structures.keySet())
.then((e, p , s) -> structures.get(s.getLoadKey()));
if (findUnexplored)
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
try {
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
if (result == null) return null;
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
return Pair.of(blockPos, result.obj());
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
return null;
}
}
@Override
protected MapCodec<? extends ChunkGenerator> codec() {
return MapCodec.unit(null);
}
@Override
public ChunkGenerator getDelegate() {
if (delegate instanceof CustomChunkGenerator chunkGenerator)
return chunkGenerator.getDelegate();
return delegate;
}
@Override
public int getMinY() {
return delegate.getMinY();
}
@Override
public int getSeaLevel() {
return delegate.getSeaLevel();
}
@Override
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
if (!structureManager.shouldGenerateStructures())
return;
var chunkPos = access.getPos();
var sectionPos = SectionPos.bottomOf(access);
var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE);
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
var loc = ResourceLocation.tryParse(key);
if (loc == null) return false;
var holder = registry.get(loc).orElse(null);
if (holder == null) return false;
var structure = holder.value();
var biomes = structure.biomes();
var start = structure.generate(
holder,
levelKey,
registryAccess,
this,
biomeSource,
structureState.randomState(),
templateManager,
structureState.getLevelSeed(),
chunkPos,
fetchReferences(structureManager, access, sectionPos, structure),
access,
biome -> ignoreBiomes || biomes.contains(biome)
);
if (!start.isValid())
return false;
BoundingBox box = start.getBoundingBox();
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
structureManager.level.getMinecraftWorld().getWorld(),
CraftStructure.minecraftToBukkit(structure),
new org.bukkit.util.BoundingBox(
box.minX(),
box.minY(),
box.minZ(),
box.maxX(),
box.maxY(),
box.maxZ()
), chunkPos.x, chunkPos.z);
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
structureManager.setStartForStructure(sectionPos, structure, start, access);
}
return true;
});
}
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
return structurestart != null ? structurestart.getReferences() : 0;
}
@Override
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) {
return delegate.createState(holderlookup, randomstate, i, conf);
}
@Override
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess);
}
@Override
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess);
}
@Override
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
}
@Override
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) {
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess);
}
@Override
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
}
@Override
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
}
@Override
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
}
@Override
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
delegate.addDebugScreenInfo(list, randomstate, blockposition);
}
@Override
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
}
@Override
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
if (!structureManager.shouldGenerateStructures())
return;
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
BlockPos blockPos = sectionPos.origin();
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
var list = structures.stream()
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
.toList();
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int wX = x + blockPos.getX();
int wZ = z + blockPos.getZ();
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
}
}
for (int j = 0; j < list.size(); j++) {
Structure structure = list.get(j);
random.setFeatureSeed(i, j, structure.step().ordinal());
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
try {
level.setCurrentlyGenerating(supplier);
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
} catch (Exception exception) {
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
CrashReportCategory category = crashReport.addCategory("Feature");
category.setDetail("Description", supplier::get);
throw new ReportedException(crashReport);
}
}
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
}
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
ChunkPos chunkPos = ichunkaccess.getPos();
int minX = chunkPos.getMinBlockX();
int minZ = chunkPos.getMinBlockZ();
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
int minY = heightAccessor.getMinY() + 1;
int maxY = heightAccessor.getMaxY();
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
}
@Override
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
delegate.spawnOriginalMobs(regionlimitedworldaccess);
}
@Override
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
return delegate.getSpawnHeight(levelheightaccessor);
}
@Override
public int getGenDepth() {
return delegate.getGenDepth();
}
@Override
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
}
@Override
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
int block = engine.getHeight(i, j, true);
int water = engine.getHeight(i, j, false);
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
for (int k = 0; k < column.length; k++) {
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
else column[k] = Blocks.AIR.defaultBlockState();
}
return new NoiseColumn(levelheightaccessor.getMinY(), column);
}
@Override
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
return delegate.getTypeNameForDataFixer();
}
@Override
public void validate() {
delegate.validate();
}
@Override
@SuppressWarnings("deprecation")
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> holder) {
return delegate.getBiomeGenerationSettings(holder);
}
static {
Field biomeSource = null;
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
if (!field.getType().equals(BiomeSource.class))
continue;
biomeSource = field;
break;
}
if (biomeSource == null)
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
Method setHeight = null;
for (Method method : Heightmap.class.getDeclaredMethods()) {
var types = method.getParameterTypes();
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|| !method.getReturnType().equals(void.class))
continue;
setHeight = method;
break;
}
if (setHeight == null)
throw new RuntimeException("Could not find setHeight method in Heightmap!");
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
}
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
try {
BIOME_SOURCE.set(generator, source);
if (generator instanceof CustomChunkGenerator custom)
BIOME_SOURCE.set(custom.getDelegate(), source);
return generator;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,858 @@
package com.volmit.iris.core.nms.v1_21_R6;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.nms.container.StructurePlacement;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
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.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.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.*;
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.CustomSpawner;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_21_R6.CraftChunk;
import org.bukkit.craftbukkit.v1_21_R6.CraftServer;
import org.bukkit.craftbukkit.v1_21_R6.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R6.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_21_R6.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_21_R6.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R6.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_21_R6.util.CraftNamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.awt.Color;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final AtomicCache<Method> 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> T fieldForClass(Class<T> 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(Material material) {
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
}
@Override
public boolean hasTile(Location l) {
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())) != null;
}
@Override
@SuppressWarnings("unchecked")
public KMap<String, Object> serializeTile(Location location) {
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
if (e == null) {
return null;
}
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry());
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
}
@Contract(value = "null, _, _ -> null", pure = true)
private Object convertFromTag(Tag tag, int depth, int maxDepth) {
if (tag == null || depth > maxDepth) return null;
return switch (tag) {
case CollectionTag collection -> {
KList<Object> list = new KList<>();
for (Object i : collection) {
if (i instanceof Tag t)
list.add(convertFromTag(t, depth + 1, maxDepth));
else list.add(i);
}
yield list;
}
case net.minecraft.nbt.CompoundTag compound -> {
KMap<String, Object> map = new KMap<>();
for (String key : compound.keySet()) {
var child = compound.get(key);
if (child == null) continue;
var value = convertFromTag(child, depth + 1, maxDepth);
if (value == null) continue;
map.put(key, value);
}
yield map;
}
case NumericTag numeric -> numeric.box();
default -> tag.asString().orElse(null);
};
}
@Override
public void deserializeTile(KMap<String, Object> map, Location pos) {
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
var level = ((CraftWorld) pos.getWorld()).getHandle();
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
J.s(() -> merge(level, blockPos, tag));
}
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
var blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
var state = level.getBlockState(blockPos);
if (!state.hasBlockEntity())
return;
blockEntity = ((EntityBlock) state.getBlock())
.newBlockEntity(blockPos, state);
}
var accessor = new BlockDataAccessor(blockEntity, blockPos);
accessor.setData(accessor.getData().merge(tag));
}
private Tag convertToTag(Object object, int depth, int maxDepth) {
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
return switch (object) {
case Map<?, ?> map -> {
var tag = new net.minecraft.nbt.CompoundTag();
for (var i : map.entrySet()) {
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
}
yield tag;
}
case List<?> list -> {
var tag = new ListTag();
for (var i : list) {
tag.add(convertToTag(i, depth + 1, maxDepth));
}
yield tag;
}
case Byte number -> ByteTag.valueOf(number);
case Short number -> ShortTag.valueOf(number);
case Integer number -> IntTag.valueOf(number);
case Long number -> LongTag.valueOf(number);
case Float number -> FloatTag.valueOf(number);
case Double number -> DoubleTag.valueOf(number);
case String string -> StringTag.valueOf(string);
default -> EndTag.INSTANCE;
};
}
@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<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
return registry().lookup(Registries.BIOME).orElse(null);
}
private Registry<Block> getBlockRegistry() {
return registry().lookup(Registries.BLOCK).orElse(null);
}
@Override
public Object getBiomeBaseFromId(int id) {
return getCustomBiomeRegistry().get(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<net.minecraft.world.level.biome.Biome>) 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().getValue(ResourceLocation.parse(mckey));
}
@Override
public Object getCustomBiomeBaseHolderFor(String mckey) {
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null);
}
public int getBiomeBaseIdForKey(String key) {
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.parse(key)).map(Holder::value).orElse(null));
}
@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().lookup(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<net.minecraft.world.level.biome.Biome>) 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<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
}
baseBiomeCache.put(biome, v);
return v;
}
@Override
public KList<Biome> 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<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null);
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
}
}
return biome.ordinal();
}
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
@NotNull
@Override
public Iterator<net.minecraft.world.level.biome.Biome> 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<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> 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<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
return getBiomeContainerInterface(getBiomeMapping(), base);
}
@Override
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> 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<Object> biomes) {
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
c.markUnsaved();
}
@Override
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
try {
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) 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<BlockState> registry = registryCache.aquireNasty(() -> {
Field cf = IdMapper.class.getDeclaredField("tToId");
Field df = IdMapper.class.getDeclaredField("idToT");
Field bf = IdMapper.class.getDeclaredField("nextId");
cf.setAccessible(true);
df.setAccessible(true);
bf.setAccessible(true);
IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
int b = bf.getInt(blockData);
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
List<BlockState> d = (List<BlockState>) df.get(blockData);
return new MCAIdMapper<BlockState>(c, d, b);
});
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
MCAPalettedContainer<BlockState> 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().get(b.getBiomeId()).get());
c.getAndIncrement();
} else {
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
r.getAndIncrement();
}
}
});
}
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
if (customNbt != null && !customNbt.isEmpty()) {
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
try {
net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString());
tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag());
s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
} catch (CommandSyntaxException var5) {
throw new IllegalArgumentException(var5);
}
return CraftItemStack.asBukkitCopy(s);
} else {
return itemStack;
}
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
worldGenContextField.setAccessible(true);
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
var newContext = new WorldGenContext(
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
worldGenContextField.set(chunkMap, newContext);
}
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<Float> 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);
}
@Override
public Color getBiomeColor(Location location, BiomeColor type) {
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
var biome = holder.value();
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
int rgba = switch (type) {
case FOG -> biome.getFogColor();
case WATER -> biome.getWaterColor();
case WATER_FOG -> biome.getWaterFogColor();
case SKY -> biome.getSkyColor();
case FOLIAGE -> biome.getFoliageColor();
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
};
if (rgba == 0) {
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
return null;
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
return null;
}
return new Color(rgba, true);
}
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<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
}
@Override
public DataVersion getDataVersion() {
return DataVersion.V1213;
}
@Override
public int getSpawnChunkCount(World world) {
return 0;
}
@Override
public KList<String> getStructureKeys() {
KList<String> keys = new KList<>();
var registry = registry().lookup(Registries.STRUCTURE).orElse(null);
if (registry == null) return keys;
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
registry.getTags()
.map(HolderSet.Named::key)
.map(TagKey::location)
.map(ResourceLocation::toString)
.map(s -> "#" + s)
.forEach(keys::add);
return keys;
}
@Override
public boolean missingDimensionTypes(String... keys) {
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
return !Arrays.stream(keys)
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
.allMatch(type::containsKey);
}
@Override
public boolean injectBukkit() {
if (injected.getAndSet(true))
return true;
try {
Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public KMap<Material, List<BlockProperty>> getBlockProperties() {
KMap<Material, List<BlockProperty>> states = new KMap<>();
for (var block : registry().lookupOrThrow(Registries.BLOCK)) {
var state = block.defaultBlockState();
if (state == null) state = block.getStateDefinition().any();
final var finalState = state;
states.put(CraftMagicNumbers.getMaterial(block), block.getStateDefinition()
.getProperties()
.stream()
.map(p -> createProperty(p, finalState))
.toList());
}
return states;
}
private <T extends Comparable<T>> BlockProperty createProperty(Property<T> property, BlockState state) {
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
}
@Override
public void placeStructures(Chunk chunk) {
var craft = ((CraftChunk) chunk);
var level = craft.getCraftWorld().getHandle();
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
}
@Override
public KMap<Identifier, StructurePlacement> collectStructures() {
var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET);
var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT);
return structureSets.keySet()
.stream()
.map(structureSets::get)
.filter(Optional::isPresent)
.map(Optional::get)
.map(holder -> {
var set = holder.value();
var placement = set.placement();
var key = holder.key().location();
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
if (placement instanceof RandomSpreadStructurePlacement random) {
builder = StructurePlacement.RandomSpread.builder()
.separation(random.separation())
.spacing(random.spacing())
.spreadType(switch (random.spreadType()) {
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
});
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
builder = StructurePlacement.ConcentricRings.builder()
.distance(rings.distance())
.spread(rings.spread())
.count(rings.count());
} else {
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
return null;
}
return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder
.salt(placement.salt)
.frequency(placement.frequency)
.structures(set.structures()
.stream()
.map(entry -> new StructurePlacement.Structure(
entry.weight(),
entry.structure()
.unwrapKey()
.map(ResourceKey::location)
.map(ResourceLocation::toString)
.orElse(null),
entry.structure().tags()
.map(TagKey::location)
.map(ResourceLocation::toString)
.toList()
))
.filter(StructurePlacement.Structure::isValid)
.toList())
.build());
})
.filter(Objects::nonNull)
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a, KMap::new));
}
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
if (!(raw instanceof PlatformChunkGenerator gen))
throw new IllegalStateException("Generator is not platform chunk generator!");
var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return new LevelStem(dimensionType, chunkGenerator(access));
}
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
return new FlatLevelSource(settings);
}
private static class ChunkAccessAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
return index >= access.getPostProcessing().length;
}
}
private static class ServerLevelAdvice {
@Advice.OnMethodEnter
static void enter(
@Advice.Argument(0) MinecraftServer server,
@Advice.Argument(3) PrimaryLevelData levelData,
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
@Advice.Argument(11) World.Environment env,
@Advice.Argument(12) ChunkGenerator gen
) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
try {
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
}
}