mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-19 15:09:18 +00:00
@@ -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,7 +28,6 @@ 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)
|
||||||
}
|
}
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
182
buildSrc/src/main/kotlin/NMSBinding.kt
Normal file
182
buildSrc/src/main/kotlin/NMSBinding.kt
Normal 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)
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<>();
|
||||||
|
|||||||
@@ -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!");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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<>();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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,41 +228,16 @@ 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 {
|
latch.countDown();
|
||||||
c = PaperLib.getChunkAtAsync(world, xx, zz, false, true)
|
generated.set(false);
|
||||||
.thenApply(chunk -> {
|
return;
|
||||||
if (chunk != null)
|
}
|
||||||
chunk.addPluginChunkTicket(Iris.instance);
|
holder.addTicket(chunk);
|
||||||
return chunk;
|
latch.countDown();
|
||||||
}).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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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) {}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
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()) {
|
buildProperties(properties, required, extended, c);
|
||||||
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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
return t;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
private static Class<?>[] getInterfaces(ClassLoader loader, Class<?> base) {
|
||||||
@Override
|
return cache.computeIfAbsent(base, k -> {
|
||||||
public String getAsString(boolean b) {
|
Queue<Class<?>> queue = new LinkedList<>();
|
||||||
return base.getAsString(b);
|
Set<Class<?>> set = new HashSet<>();
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
queue.add(k);
|
||||||
@Override
|
while (!queue.isEmpty()) {
|
||||||
public BlockData merge(@NotNull BlockData blockData) {
|
Class<?> i = queue.poll();
|
||||||
return new IrisCustomData(base.merge(blockData), custom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (!BlockData.class.isAssignableFrom(i))
|
||||||
public boolean matches(@Nullable BlockData blockData) {
|
continue;
|
||||||
if (blockData instanceof IrisCustomData b)
|
|
||||||
return custom.equals(b.custom) && base.matches(b.base);
|
|
||||||
return base.matches(blockData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
for (Class<?> j : i.getInterfaces()) {
|
||||||
@Override
|
if (j.isSealed() || j.isHidden())
|
||||||
public BlockData clone() {
|
continue;
|
||||||
return new IrisCustomData(base.clone(), custom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
try {
|
||||||
@Override
|
Class.forName(j.getName(), false, loader);
|
||||||
public SoundGroup getSoundGroup() {
|
set.add(j);
|
||||||
return base.getSoundGroup();
|
} catch (ClassNotFoundException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
var parent = i.getSuperclass();
|
||||||
public int getLightEmission() {
|
if (parent != null)
|
||||||
return base.getLightEmission();
|
queue.add(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
set.add(IrisCustomData.class);
|
||||||
public boolean isOccluding() {
|
return set.toArray(Class<?>[]::new);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
220
core/src/main/java/com/volmit/iris/util/data/VectorMap.java
Normal file
220
core/src/main/java/com/volmit/iris/util/data/VectorMap.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
Varint.writeUnsignedVarInt(length, dos);
|
write.lock();
|
||||||
Varint.writeUnsignedVarInt(palette.size(), dos);
|
try {
|
||||||
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
|
trim();
|
||||||
data.write(dos);
|
Varint.writeUnsignedVarInt(length, dos);
|
||||||
dos.flush();
|
Varint.writeUnsignedVarInt(palette.size(), dos);
|
||||||
|
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
|
||||||
|
data.write(dos);
|
||||||
|
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;
|
||||||
|
|
||||||
if (id == -1) {
|
read.lock();
|
||||||
id = palette.add(t);
|
try {
|
||||||
updateBits();
|
id = palette.id(t);
|
||||||
|
if (id == -1) {
|
||||||
|
id = palette.add(t);
|
||||||
|
if (palette.bits() == data.getBits()) {
|
||||||
|
data.set(position, id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
read.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.set(position, id);
|
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,18 +168,44 @@ public class DataContainer<T> {
|
|||||||
data = data.setBits(bits);
|
data = data.setBits(bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
public T get(int position) {
|
public T get(int position) {
|
||||||
int id = data.get(position);
|
read.lock();
|
||||||
|
try {
|
||||||
|
int id = data.get(position);
|
||||||
|
|
||||||
if (id <= 0) {
|
if (id <= 0) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return palette.get(id);
|
||||||
|
} finally {
|
||||||
|
read.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette.get(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,25 +45,23 @@ 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;
|
||||||
|
|
||||||
for (int i = 0; i < palette.length(); i++) {
|
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(lastIndex + 1);
|
||||||
a.set(i, palette.get(i));
|
for (int i = 0; i < palette.length(); i++) {
|
||||||
}
|
a.set(i, palette.get(i));
|
||||||
|
|
||||||
palette = a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
palette = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,25 +64,36 @@ 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;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
|
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
|
||||||
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
||||||
worker.setPriority(priority);
|
worker.setPriority(priority);
|
||||||
worker.setName(name + " " + ++m);
|
worker.setName(name + " " + ++m);
|
||||||
return worker;
|
return worker;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(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) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
232
core/src/main/kotlin/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.kt
vendored
Normal file
232
core/src/main/kotlin/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.kt
vendored
Normal 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) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
102
core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt
Normal file
102
core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
@@ -856,4 +856,4 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user