Compare commits

...

99 Commits

Author SHA1 Message Date
Aidan Aeternum 28c4941941 v+ 2026-06-12 21:10:21 -04:00
Aidan Aeternum d452f10fd9 Merge pull request #1241 from VolmitSoftware/dev
3.9.2
2026-06-12 21:09:46 -04:00
Julian Krings e7d89cadd8 fix compile 2026-04-12 11:58:33 +02:00
Julian Krings cddd6b16d6 add CraftEngine data provider (#1227) 2026-04-12 11:55:36 +02:00
Julian Krings b2ac4e7f93 fix invalid json for ultrawarm dimensions on 1.21.11 2026-04-12 11:55:36 +02:00
Julian Krings c2bb32459f fix ambient particles breaking datapack generation on 1.21.11 2026-04-12 11:55:35 +02:00
Brian Neumann-Fopiano 41888e33f6 Fixing fallbacks for macos Folk
Compiles fine, just stricter defaults
2026-02-11 00:01:23 -05:00
Aidan Aeternum 25fa2553e5 v+ 2026-02-02 03:28:49 -05:00
Aidan Aeternum 86f78baecf Merge pull request #1236 from VolmitSoftware/dev
3.9.1
2026-02-02 03:28:30 -05:00
Julian Krings c31158578f fix datapack generation on 1.21.11 2026-01-29 16:40:33 +01:00
Aidan Aeternum 4e86d7d634 v+ 2026-01-27 20:09:25 -05:00
Aidan Aeternum 62fe29cf34 Merge pull request #1235 from VolmitSoftware/dev
3.9.0
2026-01-27 20:08:36 -05:00
Julian Krings a3bcea4f3e increase memory assumption per region 2026-01-26 21:37:29 +01:00
Julian Krings 7befce1084 initial 1.21.11 support 2026-01-23 11:46:15 +01:00
Aidan Aeternum 351a1aa495 v+ 2026-01-15 02:50:30 -05:00
Aidan Aeternum d2cb8a9032 Merge pull request #1234 from VolmitSoftware/dev
3.8.2
2026-01-15 02:50:03 -05:00
Julian Krings 509715087c fix typo 2026-01-13 15:11:21 +01:00
Julian Krings 12527ecdd8 improve cachedChunks lookup in the mantle writer 2026-01-05 14:58:58 +01:00
Julian Krings 01a2999e03 decrease locking for mantle generation 2026-01-05 14:58:09 +01:00
Julian Krings dbafe84fa5 make context dereference method fail-safe 2026-01-02 14:12:44 +01:00
Julian Krings 40b020fc5d improve matter generator once again 2026-01-02 14:10:35 +01:00
Julian Krings d15f7d62d1 cleanup async world ticker 2026-01-02 14:10:35 +01:00
Julian Krings f9c062c794 improve mantle cleanup to prevent thread starvation 2026-01-02 14:10:34 +01:00
Aidan Aeternum 6a89b8bd06 v+ 2026-01-02 06:17:00 -05:00
Aidan Aeternum 194fcb2ea7 Merge pull request #1232 from VolmitSoftware/dev
3.8.1
2026-01-02 06:16:28 -05:00
Julian Krings 3b68f855b2 get back some speed by loading four mantle regions into cache at once 2025-12-31 13:38:42 +01:00
Julian Krings cfbf68d37a add check for headless environments to the edit command 2025-12-31 13:01:18 +01:00
Julian Krings 9999f3e429 fix chunk artifacts 2025-12-31 13:00:45 +01:00
Julian Krings 97fa0562a4 fix custom block data 2025-12-23 22:51:41 +01:00
Julian Krings 3abe671851 suppress unchecked cast warning for the ChunkedDataCache 2025-12-18 18:53:59 +01:00
Julian Krings e164a3bb5c Merge branch 'feat/safeguard' into dev
# Conflicts:
#	build.gradle.kts
#	core/src/main/java/com/volmit/iris/Iris.java
2025-12-18 18:44:36 +01:00
Julian Krings 4dfb4441e4 fix buildscript 2025-12-18 14:23:51 +01:00
Julian Krings e686f67453 add command to fix offsets of all objects 2025-12-18 14:23:35 +01:00
Julian Krings e2eed4812a ignore empty objects for shrinkwrap 2025-12-18 14:22:40 +01:00
Julian Krings 1737833bfe fix legacy object parsing 2025-12-18 14:22:21 +01:00
piexel a4c5f46c37 Fix /iris object convert for .schem files with 128+ block types 2025-12-18 11:25:38 +01:00
piexel 025fa833c4 Fix Grass won't place on moss blocks 2025-12-18 11:21:16 +01:00
Julian Krings 98cc82cc3d Fix jigsaw editor bricking when opening a piece a missing object 2025-12-18 11:13:43 +01:00
Julian Krings 298365f588 fix studio open not working when not in spectator 2025-12-18 11:06:09 +01:00
Julian Krings 90e5720e2e improve buildscript 2025-12-18 11:05:28 +01:00
Julian Krings 7cd43791f4 fix offset with shrinkwrap 2025-12-18 11:05:14 +01:00
Julian Krings a251d192ad fix loot for objects underground 2025-12-18 11:00:04 +01:00
Julian Krings abfff28f43 fix redstone not rotating 2025-12-18 10:59:57 +01:00
Julian Krings fbdae4c928 improve overwriting of the regenerated chunks 2025-12-18 10:59:32 +01:00
Aidan Aeternum 93ca26e368 v+ 2025-12-17 12:12:25 -05:00
Aidan Aeternum 8c1db1c223 Merge pull request #1231 from VolmitSoftware/dev
3.8.0
2025-12-17 12:11:28 -05:00
Julian Krings 123708601f improve pregen cache 2025-12-01 16:01:54 +01:00
Julian Krings b5ab4968ba fix compile 2025-11-30 15:36:36 +01:00
Julian Krings 93ae6037bd whoops forgot to disable craftbukkit relocations on <=1.20.4 2025-11-29 11:49:15 +01:00
Julian Krings dd8e487a3b fix tile entity serialization on paper 1.21.6+ servers 2025-11-29 11:13:30 +01:00
Julian Krings d3c8377a12 allow switching between buildtools and userdev 2025-11-29 11:12:16 +01:00
Julian Krings 84f3687c0c update compat for iron bars 2025-11-27 16:09:31 +01:00
Julian Krings ed1e1f1181 whoops forgot those providers 2025-11-26 21:33:03 +01:00
Julian Krings 528c97f367 replace IrisCustomData class with a proxy to keep full access to the base BlockData 2025-11-26 21:32:13 +01:00
Julian Krings f7a459f3bc initial 1.21.9/10 bindings 2025-11-26 21:25:05 +01:00
Julian Krings cd179b4321 reintroduce native threads for the updater as a setting and cleanup 2025-11-26 20:55:48 +01:00
Julian Krings 6373dbb1b8 potential optimization for the noise cache 2025-11-21 16:21:54 +01:00
Julian Krings 0b0797f876 minor optimization for chunk updater 2025-11-21 16:21:31 +01:00
Julian Krings 446acefc91 minor optimization for the mantle writer 2025-11-21 16:21:02 +01:00
Julian Krings 7a44e555b2 rename noise cache size setting and decrease default value 2025-11-21 16:20:32 +01:00
Julian Krings 57d4c2935c add cache sizes to engine status 2025-11-21 16:16:15 +01:00
Julian Krings 234fb1b0c4 also include parent class for schema generation 2025-11-08 00:58:12 +01:00
Julian Krings 0882b5acc4 use parent shared classloader to reflect intelij behavior 2025-11-06 22:32:28 +01:00
Julian Krings 18da26e1fa minor performance improvement 2025-11-06 22:27:14 +01:00
Julian Krings 65aa95f2a5 fix wrong radius when marking mantle chunks as completed 2025-11-06 22:25:37 +01:00
Julian Krings 5330ddc4ec fix deleting object ids with mantle cleanup 2025-11-06 22:20:30 +01:00
Julian Krings a7fdd37569 fix studio loot command 2025-11-06 22:16:27 +01:00
Julian Krings a226fea9e2 fix regen command 2025-11-06 16:22:02 +01:00
Julian Krings 8cea165a29 even more performance improvements 2025-11-06 15:29:03 +01:00
Julian Krings 1d7cba184c fix linear palette not growing correctly 2025-11-06 14:41:39 +01:00
Julian Krings 4ca7ea3911 minor speed improvements 2025-11-01 22:36:25 +01:00
Julian Krings ea5919def2 stop writing first access each time the engine is opened 2025-10-30 16:40:35 +01:00
Julian Krings be35e49302 use coroutines for mantle generation 2025-10-30 16:40:08 +01:00
Julian Krings aadd03990a optimize data palette for mantle slices 2025-10-27 19:53:47 +01:00
Julian Krings 38a579453d optimize noise cache 2025-10-26 13:49:03 +01:00
Julian Krings 0bf5da2ca1 optimize object maps 2025-10-26 13:48:11 +01:00
Julian Krings 317848692e improve regen speed 2025-10-06 14:12:01 +02:00
Julian Krings 22118de9e9 fix object smart bore 2025-10-06 14:10:37 +02:00
Julian Krings d7039d120b fix place commands causing unwanted block updates 2025-10-06 13:00:00 +02:00
Julian Krings 979ee4e7d8 switch hashing algorithm for objects once more 2025-10-05 21:46:12 +02:00
Julian Krings f68d45bd30 dynamically resolve snippet classes 2025-10-05 00:21:38 +02:00
Julian Krings b86d7f303e restructure the shared kts classloader to be more consistent 2025-10-05 00:20:55 +02:00
Julian Krings c573843314 fix kts dependency resolver 2025-10-05 00:17:57 +02:00
Julian Krings 51a7bef18e whoops forgot escaping the path 2025-10-04 17:19:11 +02:00
Julian Krings 0e237aa1ad fix generated build.gradle.kts on external dives on windows 2025-10-04 13:41:48 +02:00
Julian Krings b46c413f6b make gradle setup print to console on failure 2025-10-04 13:40:47 +02:00
Julian Krings f3ef1ca2ae fix typo in preprocessors description 2025-10-04 13:40:07 +02:00
Julian Krings 703e61dd54 fix preprocessors not applying reliably 2025-10-04 13:39:33 +02:00
Aidan Aeternum e1ec6b7827 v+ 2025-10-03 16:08:05 -04:00
Aidan Aeternum f94292fdac Merge pull request #1228 from VolmitSoftware/dev
3.7.11
2025-10-03 16:07:47 -04:00
Julian Krings 7d153bf985 optimize objects to avoid hash collision 2025-10-02 15:25:37 +02:00
Julian Krings f85f15ed02 fix converter for complex schematic 2025-10-02 11:43:16 +02:00
Julian Krings 867686eced fix deserialisation of unversioned mantle plates 2025-10-01 12:57:38 +02:00
Julian Krings 526efd3ae1 restructure safeguard 2025-09-29 15:45:56 +02:00
Aidan Aeternum 9d796bd2a0 v+ 2025-09-27 07:34:58 -04:00
Aidan Aeternum 1a9a5d80ad Merge pull request #1223 from VolmitSoftware/dev
3.7.10
2025-09-27 07:34:31 -04:00
Julian Krings c5c7f9bdc5 fix minor issue with nms tools 2025-09-22 18:10:34 +02:00
Julian Krings 01a421b732 add file extensions to the script property descriptions 2025-09-21 22:35:34 +02:00
Julian Krings ae92bcf194 add kts hook for chunk updates 2025-09-21 22:30:48 +02:00
138 changed files with 7225 additions and 2561 deletions
+46 -34
View File
@@ -1,6 +1,5 @@
import com.volmit.nmstools.NMSToolsExtension
import com.volmit.nmstools.NMSToolsPlugin
import de.undercouch.gradle.tasks.download.Download
import org.gradle.jvm.toolchain.JavaLanguageVersion
import xyz.jpenilla.runpaper.task.RunServer
import kotlin.system.exitProcess
@@ -24,19 +23,18 @@ import kotlin.system.exitProcess
buildscript {
repositories.maven("https://jitpack.io")
dependencies.classpath("com.github.VolmitSoftware:NMSTools:c5cbc46ce6")
dependencies.classpath("com.github.VolmitSoftware:NMSTools:c88961416f")
}
plugins {
java
`java-library`
alias(libs.plugins.shadow)
alias(libs.plugins.download)
alias(libs.plugins.runPaper)
}
group = "com.volmit"
version = "3.7.2-1.20.1-1.21.8"
version = "3.9.2-1.20.1-1.21.11"
apply<ApiGenerator>()
@@ -53,41 +51,40 @@ registerCustomOutputTask("PixelFury", "C://Users/repix/workplace/Iris/1.21.3 - D
registerCustomOutputTask("PixelFuryDev", "C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins")
// ========================== UNIX ==============================
registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins")
registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/Server/plugins")
registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/plugins")
registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins")
registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins")
// ==============================================================
val serverMinHeap = "2G"
val serverMaxHeap = "8G"
val serverMinHeap = "10G"
val serverMaxHeap = "10G"
val additionalFlags = "-XX:+AlwaysPreTouch"
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
val color = "truecolor"
val errorReporting = findProperty("errorReporting") as Boolean? ?: false
val errorReporting = "true" == findProperty("errorReporting")
val nmsBindings = mapOf(
"v1_21_R5" to "1.21.7-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_R2" to "1.21.3-R0.1-SNAPSHOT",
"v1_21_R1" to "1.21.1-R0.1-SNAPSHOT",
"v1_20_R4" to "1.20.6-R0.1-SNAPSHOT",
"v1_20_R3" to "1.20.4-R0.1-SNAPSHOT",
"v1_20_R2" to "1.20.2-R0.1-SNAPSHOT",
"v1_20_R1" to "1.20.1-R0.1-SNAPSHOT",
"v1_21_R7" to "1.21.11-R0.1-SNAPSHOT",
"v1_21_R6" to "1.21.10-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_R3" to "1.21.4-R0.1-SNAPSHOT",
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
"v1_21_R1" to "1.21.1-R0.1-SNAPSHOT",
"v1_20_R4" to "1.20.6-R0.1-SNAPSHOT",
"v1_20_R3" to "1.20.4-R0.1-SNAPSHOT",
"v1_20_R2" to "1.20.2-R0.1-SNAPSHOT",
"v1_20_R1" to "1.20.1-R0.1-SNAPSHOT",
)
val jvmVersion = mapOf<String, Int>()
nmsBindings.forEach { key, value ->
nmsBindings.forEach { (key, value) ->
project(":nms:$key") {
apply<JavaPlugin>()
apply<NMSToolsPlugin>()
repositories {
maven("https://libraries.minecraft.net")
}
extensions.configure(NMSToolsExtension::class) {
nmsBinding {
jvm = jvmVersion.getOrDefault(key, 21)
version = value
type = NMSBinding.Type.DIRECT
}
dependencies {
@@ -110,17 +107,25 @@ nmsBindings.forEach { key, value ->
systemProperty("com.mojang.eula.agree", true)
systemProperty("iris.suppressReporting", !errorReporting)
jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}")
jvmArgs(additionalFlags.split(' '))
}
}
val included: Configuration by configurations.creating
val jarJar: Configuration by configurations.creating
dependencies {
for (key in nmsBindings.keys) {
included(project(":nms:$key", "reobf"))
}
included(project(":core", "shadow"))
jarJar(project(":core:agent"))
}
tasks {
jar {
inputs.files(included)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
nmsBindings.forEach { key, _ ->
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 })
from(jarJar, provider { included.resolve().map(::zipTree) })
archiveFileName.set("Iris-${project.version}.jar")
}
@@ -183,6 +188,12 @@ configurations.configureEach {
allprojects {
apply<JavaPlugin>()
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
@@ -195,6 +206,7 @@ allprojects {
maven("https://mvn.lumine.io/repository/maven-public/") // mythic
maven("https://nexus.phoenixdevt.fr/repository/maven-public/") //MMOItems
maven("https://repo.onarandombox.com/content/groups/public/") //Multiverse Core
maven("https://repo.momirealms.net/releases/") //CraftEngine
}
dependencies {
@@ -230,14 +242,14 @@ allprojects {
}
}
if (JavaVersion.current().toString() != "21") {
if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) {
System.err.println()
System.err.println("=========================================================================================================")
System.err.println("You must run gradle on Java 21. You are using " + JavaVersion.current())
System.err.println("You must run gradle on Java 21 or newer. You are using " + JavaVersion.current())
System.err.println()
System.err.println("=== For IDEs ===")
System.err.println("1. Configure the project for Java 21")
System.err.println("2. Configure the bundled gradle to use Java 21 in settings")
System.err.println("1. Configure the project for Java 21 toolchain")
System.err.println("2. Configure the bundled gradle to use Java 21+ in settings")
System.err.println()
System.err.println("=== For Command Line (gradlew) ===")
System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html")
+23 -2
View File
@@ -1,11 +1,32 @@
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm") version "2.0.20"
kotlin("jvm") version embeddedKotlinVersion
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
kotlin {
jvmToolchain(21)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://jitpack.io")
}
dependencies {
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
View File
@@ -0,0 +1,182 @@
import NMSBinding.Type
import com.volmit.nmstools.NMSToolsExtension
import com.volmit.nmstools.NMSToolsPlugin
import io.papermc.paperweight.userdev.PaperweightUser
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
import io.papermc.paperweight.userdev.PaperweightUserExtension
import io.papermc.paperweight.userdev.attribute.Obfuscation
import io.papermc.paperweight.util.constants.REOBF_CONFIG
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.gradle.api.*
import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.TaskAction
import org.gradle.internal.extensions.core.extra
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.work.DisableCachingByDefault
import java.io.RandomAccessFile
import javax.inject.Inject
class NMSBinding : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
val config = extra["nms"] as? Config ?: throw GradleException("No NMS binding configuration found")
val jvm = config.jvm
val type = config.type
if (type == Type.USER_DEV) {
plugins.apply(PaperweightUser::class.java)
dependencies.extensions.findByType(PaperweightUserDependenciesExtension::class.java)
?.paperDevBundle(config.version)
val java = extensions.findByType(JavaPluginExtension::class.java) ?: throw GradleException("Java plugin not found")
java.toolchain.languageVersion.set(JavaLanguageVersion.of(jvm))
val javaToolchains = project.extensions.getByType(JavaToolchainService::class.java) ?: throw GradleException("Java toolchain service not found")
extensions.configure(PaperweightUserExtension::class.java) {
it.javaLauncher.set(javaToolchains.launcherFor(java.toolchain))
}
} else {
extra["nmsTools.useBuildTools"] = type == Type.BUILD_TOOLS
plugins.apply(NMSToolsPlugin::class.java)
extensions.configure(NMSToolsExtension::class.java) {
it.jvm.set(jvm)
it.version.set(config.version)
}
configurations.register(REOBF_CONFIG) { conf ->
conf.isCanBeConsumed = true
conf.isCanBeResolved = false
conf.attributes {
it.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
it.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
it.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
it.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
it.attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED))
}
conf.outgoing.artifact(tasks.named("remap"))
}
}
val (major, minor) = config.version.parseVersion()
if (major <= 20 && minor <= 4) return@with
tasks.register("convert", ConversionTask::class.java, type)
tasks.named("compileJava") { it.dependsOn("convert") }
rootProject.tasks.named("prepareKotlinBuildScriptModel") { it.dependsOn("$path:convert") }
}
@DisableCachingByDefault
abstract class ConversionTask @Inject constructor(type: Type) : DefaultTask() {
private val pattern: Regex
private val replacement: String
init {
group = "nms"
inputs.property("type", type)
val java = project.extensions.findByType(JavaPluginExtension::class.java) ?: throw GradleException("Java plugin not found")
val source = java.sourceSets.named("main").map { it.allJava }
inputs.files(source)
outputs.files(source)
if (type == Type.USER_DEV) {
pattern = "org\\.bukkit\\.craftbukkit\\.${project.name}".toRegex()
replacement = "org.bukkit.craftbukkit"
} else {
pattern = "org\\.bukkit\\.craftbukkit\\.(?!${project.name})".toRegex()
replacement = "org.bukkit.craftbukkit.${project.name}."
}
}
@TaskAction
fun process() {
val dispatcher = Dispatchers.IO.limitedParallelism(16)
runBlocking {
for (file in inputs.files) {
if (file.extension !in listOf("java"))
continue
launch(dispatcher) {
val output = ArrayList<String>()
var changed = false
file.bufferedReader().use {
for (line in it.lines()) {
if (line.startsWith("package") || line.isBlank()) {
output += line
continue
}
if (!line.startsWith("import")) {
if (!changed) return@launch
else {
output += line
continue
}
}
if (!line.contains(pattern)) {
output += line
continue
}
output += line.replace(pattern, replacement)
changed = true
}
}
if (changed) {
RandomAccessFile(file, "r").use { raf ->
val bytes = ByteArray(NEW_LINE_BYTES.size)
raf.seek(raf.length() - bytes.size)
raf.readFully(bytes)
if (bytes.contentEquals(NEW_LINE_BYTES))
output += ""
}
file.writer().use {
val iterator = output.iterator()
while (iterator.hasNext()) {
it.append(iterator.next())
if (iterator.hasNext())
it.append(NEW_LINE)
}
}
}
}
}
}
}
}
enum class Type {
USER_DEV,
BUILD_TOOLS,
DIRECT,
}
}
private val NEW_LINE = System.lineSeparator()
private val NEW_LINE_BYTES = NEW_LINE.encodeToByteArray()
private fun String.parseVersion() = substringBefore('-').split(".").let {
it[1].toInt() to it[2].toInt()
}
class Config(
var jvm: Int = 21,
var type: Type = Type.DIRECT
) {
lateinit var version: String
}
fun Project.nmsBinding(action: Config.() -> Unit) {
extra["nms"] = Config().apply(action)
plugins.apply(NMSBinding::class.java)
}
private inline fun <reified T : Named> ObjectFactory.named(name: String): T = named(T::class.java, name)
+31 -7
View File
@@ -1,6 +1,8 @@
import io.github.slimjar.func.slimjarHelper
import io.github.slimjar.resolver.data.Mirror
import org.ajoberstar.grgit.Grgit
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.net.URI
/*
@@ -67,6 +69,9 @@ dependencies {
isTransitive = false
}
compileOnly(libs.multiverseCore)
compileOnly(libs.craftengine.core)
compileOnly(libs.craftengine.bukkit)
//compileOnly(libs.sparrowNbt)
// Shaded
implementation(slimjarHelper("spigot"))
@@ -112,10 +117,20 @@ dependencies {
java {
disableAutoTargetJvm()
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
kotlin {
jvmToolchain(21)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
sentry {
url = "http://sentry.volmit.com:8080/"
url = "http://sentry.volmit.com:8080"
autoInstallation.enabled = false
includeSourceContext = true
@@ -142,6 +157,7 @@ slimJar {
relocate("com.google.inject", "$lib.guice")
relocate("org.dom4j", "$lib.dom4j")
relocate("org.jaxen", "$lib.jaxen")
relocate("com.github.benmanes.caffeine", "$lib.caffeine")
}
tasks {
@@ -174,20 +190,28 @@ tasks {
relocate("io.github.slimjar", "$lib.slimjar")
exclude("modules/loader-agent.isolated-jar")
}
sentryCollectSourcesJava {
dependsOn(generateTemplates)
}
}
val templateSource = file("src/main/templates")
val templateDest = layout.buildDirectory.dir("generated/sources/templates")
val templateDest = layout.buildDirectory.dir("generated/sources/templates")!!
val generateTemplates = tasks.register<Copy>("generateTemplates") {
inputs.properties(
"environment" to if (project.hasProperty("release")) "production" else "development",
"environment" to when {
project.hasProperty("release") -> "production"
project.hasProperty("argghh") -> "Argghh!"
else -> "development"
},
"commit" to provider {
val res = runCatching { project.extensions.getByType<Grgit>().head().id }
res.getOrDefault("")
.takeIf { it.length == 40 } ?: {
logger.error("Git commit hash not found", res.exceptionOrNull())
.takeIf { it.length == 40 } ?: run {
this.logger.error("Git commit hash not found", res.exceptionOrNull())
"unknown"
}()
}
},
)
@@ -207,4 +231,4 @@ rootProject.tasks.named("prepareKotlinBuildScriptModel") {
sourceSets.main {
java.srcDir(generateTemplates.map { it.outputs })
}
}
+12 -137
View File
@@ -28,7 +28,6 @@ import com.volmit.iris.core.link.IrisPapiExpansion;
import com.volmit.iris.core.link.MultiverseCoreLink;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.core.pregenerator.LazyPregenerator;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisToolbelt;
@@ -43,7 +42,6 @@ import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.exceptions.IrisException;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.NastyRunnable;
import com.volmit.iris.util.io.FileWatcher;
import com.volmit.iris.util.io.IO;
@@ -53,11 +51,11 @@ import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.misc.Bindings;
import com.volmit.iris.util.misc.SlimJar;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.plugin.VolmitPlugin;
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.Queue;
import com.volmit.iris.util.scheduling.ShurikenQueue;
@@ -83,9 +81,6 @@ import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
import static com.volmit.iris.core.safeguard.ServerBootSFG.passedserversoftware;
@SuppressWarnings("CanBeFinal")
public class Iris extends VolmitPlugin implements Listener {
private static final Queue<Runnable> syncJobs = new ShurikenQueue<>();
@@ -95,6 +90,7 @@ public class Iris extends VolmitPlugin implements Listener {
public static MultiverseCoreLink linkMultiverseCore;
public static IrisCompat compat;
public static FileWatcher configWatcher;
public static ChunkTickets tickets;
private static VolmitSender sender;
private static Thread shutdownHook;
@@ -306,9 +302,6 @@ public class Iris extends VolmitPlugin implements Listener {
public static void info(String format, Object... args) {
msg(C.WHITE + String.format(format, args));
}
public static void safeguard(String format, Object... args) {
msg(C.RESET + String.format(format, args));
}
@SuppressWarnings("deprecation")
public static void later(NastyRunnable object) {
@@ -418,6 +411,7 @@ public class Iris extends VolmitPlugin implements Listener {
pw.println();
pw.println();
}
pw.println("[%%__USER__%%,%%__RESOURCE__%%,%%__PRODUCT__%%,%%__BUILTBYBIT__%%]");
pw.close();
Iris.info("DUMPED! See " + fi.getAbsolutePath());
@@ -447,16 +441,16 @@ public class Iris extends VolmitPlugin implements Listener {
IO.delete(new File("iris"));
compat = IrisCompat.configured(getDataFile("compat.json"));
ServerConfigurator.configure();
IrisSafeguard.IrisSafeguardSystem();
IrisSafeguard.execute();
getSender().setTag(getTag());
IrisSafeguard.splash(true);
IrisSafeguard.splash();
tickets = new ChunkTickets();
linkMultiverseCore = new MultiverseCoreLink();
configWatcher = new FileWatcher(getDataFile("settings.json"));
services.values().forEach(IrisService::onEnable);
services.values().forEach(this::registerListener);
addShutdownHook();
J.s(() -> {
J.a(IrisSafeguard::suggestPaper);
J.a(() -> IO.delete(getTemp()));
J.a(LazyPregenerator::loadLazyGenerators, 100);
J.a(this::bstats);
@@ -464,7 +458,6 @@ public class Iris extends VolmitPlugin implements Listener {
J.sr(this::tickQueue, 0);
J.s(this::setupPapi);
J.a(ServerConfigurator::configure, 20);
IrisSafeguard.splash(false);
autoStartStudio();
checkForBukkitWorlds(s -> true);
@@ -526,9 +519,10 @@ public class Iris extends VolmitPlugin implements Listener {
Player r = new KList<>(getServer().getOnlinePlayers()).getRandom();
Iris.service(StudioSVC.class).open(r != null ? new VolmitSender(r) : getSender(), 1337, IrisSettings.get().getGenerator().getDefaultWorldType(), (w) -> {
J.s(() -> {
var spawn = w.getSpawnLocation();
for (Player i : getServer().getOnlinePlayers()) {
i.setGameMode(GameMode.SPECTATOR);
i.teleport(new Location(w, 0, 200, 0));
i.teleport(spawn);
}
});
});
@@ -557,10 +551,10 @@ public class Iris extends VolmitPlugin implements Listener {
enable();
super.onEnable();
Bukkit.getPluginManager().registerEvents(this, this);
setupChecks();
}
public void onDisable() {
if (IrisSafeguard.isForceShutdown()) return;
services.values().forEach(IrisService::onDisable);
Bukkit.getScheduler().cancelTasks(this);
HandlerList.unregisterAll((Plugin) this);
@@ -588,49 +582,7 @@ public class Iris extends VolmitPlugin implements Listener {
@Override
public String getTag(String subTag) {
if (unstablemode) {
return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.RED + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": ";
}
if (warningmode) {
return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.GOLD + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": ";
}
return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.IRIS + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": ";
}
private boolean setupChecks() {
boolean passed = true;
Iris.info("Version Information: " + instance.getServer().getVersion() + " | " + instance.getServer().getBukkitVersion());
if (INMS.get() instanceof NMSBinding1X) {
passed = false;
Iris.warn("============================================");
Iris.warn("=");
Iris.warn("=");
Iris.warn("=");
Iris.warn("Iris is not compatible with this version of Minecraft.");
Iris.warn("=");
Iris.warn("=");
Iris.warn("=");
Iris.warn("============================================");
}
try {
Class.forName("io.papermc.paper.configuration.PaperConfigurations");
} catch (ClassNotFoundException e) {
Iris.info(C.RED + "Iris requires paper or above to function properly..");
return false;
}
try {
Class.forName("org.purpurmc.purpur.PurpurConfig");
} catch (ClassNotFoundException e) {
Iris.info("We recommend using Purpur for the best experience with Iris.");
Iris.info("Purpur is a fork of Paper that is optimized for performance and stability.");
Iris.info("Plugins that work on Spigot / Paper work on Purpur.");
Iris.info("You can download it here: https://purpurmc.org");
return false;
}
return passed;
return IrisSafeguard.mode().tag(subTag);
}
private void checkConfigHotload() {
@@ -738,88 +690,11 @@ public class Iris extends VolmitPlugin implements Listener {
}
public void splash() {
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
return;
}
String padd = Form.repeat(" ", 8);
String padd2 = Form.repeat(" ", 4);
String[] info = {"", "", "", "", "", padd2 + C.IRIS + " Iris", padd2 + C.GRAY + " by " + "<rainbow>Volmit Software", padd2 + C.GRAY + " v" + C.IRIS + getDescription().getVersion()};
if (unstablemode) {
info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()};
}
if (warningmode) {
info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()};
}
String[] splashstable = {
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.IRIS + " .(((()))). ",
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.IRIS + " .((((((())))))). ",
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.IRIS + " ((((((((())))))))) " + C.GRAY + " @",
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.IRIS + " ((((((((-))))))))) " + C.GRAY + " @@",
padd + C.GRAY + "@@@&&" + C.IRIS + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
padd + C.GRAY + "@@" + C.IRIS + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
padd + C.GRAY + "@" + C.IRIS + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
padd + C.GRAY + "" + C.IRIS + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
padd + C.GRAY + "" + C.IRIS + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
};
String[] splashunstable = {
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.RED + " .(((()))). ",
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.RED + " .((((((())))))). ",
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.RED + " ((((((((())))))))) " + C.GRAY + " @",
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.RED + " ((((((((-))))))))) " + C.GRAY + " @@",
padd + C.GRAY + "@@@&&" + C.RED + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
padd + C.GRAY + "@@" + C.RED + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
padd + C.GRAY + "@" + C.RED + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
padd + C.GRAY + "" + C.RED + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
padd + C.GRAY + "" + C.RED + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
};
String[] splashwarning = {
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.GOLD + " .(((()))). ",
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.GOLD + " .((((((())))))). ",
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.GOLD + " ((((((((())))))))) " + C.GRAY + " @",
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.GOLD + " ((((((((-))))))))) " + C.GRAY + " @@",
padd + C.GRAY + "@@@&&" + C.GOLD + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
padd + C.GRAY + "@@" + C.GOLD + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
padd + C.GRAY + "@" + C.GOLD + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
padd + C.GRAY + "" + C.GOLD + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
padd + C.GRAY + "" + C.GOLD + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
};
String[] splash;
File freeSpace = new File(Bukkit.getWorldContainer() + ".");
if (unstablemode) {
splash = splashunstable;
} else if (warningmode) {
splash = splashwarning;
} else {
splash = splashstable;
}
if (!passedserversoftware) {
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
Iris.info("Java: " + getJava());
if (getHardware.getProcessMemory() < 5999) {
Iris.warn("6GB+ Ram is recommended");
Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB");
}
Iris.info("Bukkit distro: " + Bukkit.getName());
Iris.info("Server type & version: " + Bukkit.getName() + " v" + Bukkit.getVersion());
Iris.info("Custom Biomes: " + INMS.get().countCustomBiomes());
setupChecks();
printPacks();
for (int i = 0; i < info.length; i++) {
splash[i] += info[i];
}
Iris.info("\n\n " + new KList<>(splash).toString("\n") + "\n");
IrisSafeguard.mode().trySplash();
}
private void printPacks() {
@@ -159,7 +159,7 @@ public class IrisSettings {
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
public boolean trimMantleInStudio = false;
public int mantleKeepAlive = 30;
public int cacheSize = 4_096;
public int noiseCacheSize = 1_024;
public int resourceLoaderCacheSize = 1_024;
public int objectLoaderCacheSize = 4_096;
public int scriptLoaderCacheSize = 512;
@@ -170,13 +170,16 @@ public class IrisSettings {
if (tectonicPlateSize > 0)
return tectonicPlateSize;
return (int) (getHardware.getProcessMemory() / 200L);
return (int) (getHardware.getProcessMemory() / 512L);
}
}
@Data
public static class IrisSettingsUpdater {
public int maxConcurrency = 256;
public boolean nativeThreads = false;
public double threadMultiplier = 2;
public double chunkLoadSensitivity = 0.7;
public MsRange emptyMsRange = new MsRange(80, 100);
public MsRange defaultMsRange = new MsRange(20, 40);
@@ -185,6 +188,10 @@ public class IrisSettings {
return Math.max(Math.abs(maxConcurrency), 1);
}
public double getThreadMultiplier() {
return Math.min(Math.abs(threadMultiplier), 0.1);
}
public double getChunkLoadSensitivity() {
return Math.min(chunkLoadSensitivity, 0.9);
}
@@ -243,6 +250,7 @@ public class IrisSettings {
public int maxBiomeChildDepth = 4;
public boolean preventLeafDecay = true;
public boolean useMulticore = false;
public boolean useMulticoreMantle = false;
public boolean offsetNoiseTypes = false;
public boolean earlyCustomBlocks = false;
}
@@ -31,7 +31,9 @@ import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisPosition;
import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
@@ -52,6 +54,7 @@ import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.jobs.Job;
import lombok.SneakyThrows;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
@@ -92,6 +95,124 @@ public class CommandDeveloper implements DecreeExecutor {
Iris.reportError(new Exception("This is a test"));
}
@Decree(description = "Dev cmd to fix all the broken objects caused by faulty shrinkwarp")
public void fixObjects(
@Param(aliases = "dimension", description = "The dimension type to create the world with")
IrisDimension type
) {
if (type == null) {
sender().sendMessage("Type cant be null?");
return;
}
IrisData dm = IrisData.get(Iris.instance.getDataFolder("packs", type.getLoadKey()));
var loader = dm.getObjectLoader();
var processed = new KMap<String, IrisPosition>();
var objects = loader.getPossibleKeys();
var pieces = dm.getJigsawPieceLoader().getPossibleKeys();
var sender = sender();
sender.sendMessage(C.IRIS + "Found " + objects.length + " objects in " + type.getLoadKey());
sender.sendMessage(C.IRIS + "Found " + pieces.length + " jigsaw pieces in " + type.getLoadKey());
final int total = objects.length;
final AtomicInteger completed = new AtomicInteger();
final AtomicInteger changed = new AtomicInteger();
new Job() {
@Override
public String getName() {
return "Fixing Objects";
}
@Override
public void execute() {
Arrays.stream(pieces).parallel()
.map(dm.getJigsawPieceLoader()::load)
.filter(Objects::nonNull)
.forEach(piece -> {
var offset = processed.compute(piece.getObject(), (key, o) -> {
if (o != null) return o;
var obj = loader.load(key);
if (obj == null) return new IrisPosition();
obj.shrinkwrap();
try {
if (!obj.getShrinkOffset().isZero()) {
changed.incrementAndGet();
obj.write(obj.getLoadFile());
}
completeWork();
} catch (IOException e) {
Iris.error("Failed to write object " + obj.getLoadKey());
e.printStackTrace();
return new IrisPosition();
}
return new IrisPosition(obj.getShrinkOffset());
});
if (offset.getX() == 0 && offset.getY() == 0 && offset.getZ() == 0)
return;
piece.getConnectors().forEach(connector -> connector.setPosition(connector.getPosition().add(offset)));
try {
IO.writeAll(piece.getLoadFile(), dm.getGson().toJson(piece));
} catch (IOException e) {
Iris.error("Failed to write jigsaw piece " + piece.getLoadKey());
e.printStackTrace();
}
});
Arrays.stream(loader.getPossibleKeys()).parallel()
.filter(key -> !processed.containsKey(key))
.map(loader::load)
.forEach(obj -> {
if (obj == null) {
completeWork();
return;
}
obj.shrinkwrap();
if (obj.getShrinkOffset().isZero()) {
completeWork();
return;
}
try {
obj.write(obj.getLoadFile());
completeWork();
changed.incrementAndGet();
} catch (IOException e) {
Iris.error("Failed to write object " + obj.getLoadKey());
e.printStackTrace();
}
});
}
@Override
public void completeWork() {
completed.incrementAndGet();
}
@Override
public int getTotalWork() {
return total;
}
@Override
public int getWorkCompleted() {
return completed.get();
}
}.execute(sender, () -> {
var failed = total - completed.get();
if (failed != 0) sender.sendMessage(C.IRIS + "" + failed + " objects failed!");
if (changed.get() != 0) sender.sendMessage(C.IRIS + "" + changed.get() + " objects had their offsets changed!");
else sender.sendMessage(C.IRIS + "No objects had their offsets changed!");
});
}
@Decree(description = "Test")
public void mantle(@Param(defaultValue = "false") boolean plate, @Param(defaultValue = "21474836474") String name) throws Throwable {
var base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
@@ -46,6 +46,16 @@ public class CommandEdit implements DecreeExecutor {
sender().sendMessage(C.RED + "You must be in a studio world!");
return true;
}
if (GraphicsEnvironment.isHeadless()) {
sender().sendMessage(C.RED + "Cannot open files in headless environments!");
return true;
}
if (!Desktop.isDesktopSupported()) {
sender().sendMessage(C.RED + "Desktop is not supported by this environment!");
return true;
}
return 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.math.Direction;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.Queue;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@@ -79,9 +78,9 @@ public class CommandObject implements DecreeExecutor {
futureBlockChanges.put(block, block.getBlockData());
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!");
} else block.setBlockData(d);
} else block.setBlockData(d, false);
}
@Override
@@ -124,6 +123,16 @@ public class CommandObject implements DecreeExecutor {
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
public Engine getEngine() {
return null;
@@ -140,7 +149,7 @@ public class CommandObject implements DecreeExecutor {
sender().sendMessage("Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD() + "");
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<BlockData, Integer> amounts = 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.O;
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 org.bukkit.*;
import org.bukkit.event.inventory.InventoryType;
@@ -78,6 +78,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@@ -174,76 +175,55 @@ public class CommandStudio implements DecreeExecutor {
PlatformChunkGenerator plat = IrisToolbelt.access(world);
Engine engine = plat.getEngine();
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 z = loc.getBlockZ() >> 4;
int rad = engine.getMantle().getRadius();
var mantle = engine.getMantle().getMantle();
var chunkMap = new KMap<Position2, MantleChunk>();
ParallelQueueJob<Position2> prep = new ParallelQueueJob<>() {
ParallelRadiusJob prep = new ParallelRadiusJob(Integer.MAX_VALUE, service) {
@Override
public void execute(Position2 pos) {
var cpos = pos.add(x, z);
if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) {
mantle.deleteChunk(cpos.getX(), cpos.getZ());
protected void execute(int rX, int rZ) {
if (Math.abs(rX) <= radius && Math.abs(rZ) <= radius) {
mantle.deleteChunk(rX + x, rZ + z);
return;
}
chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ()));
mantle.deleteChunk(cpos.getX(), cpos.getZ());
rX += x;
rZ += z;
chunkMap.put(new Position2(rX, rZ), mantle.getChunk(rX, rZ));
mantle.deleteChunk(rX, rZ);
}
@Override
public String getName() {
return "Preparing Mantle";
}
};
for (int xx = -(radius + rad); xx <= radius + rad; xx++) {
for (int zz = -(radius + rad); zz <= radius + rad; zz++) {
prep.queue(new Position2(xx, zz));
}
}
}.retarget(radius + rad, 0, 0);
CountDownLatch pLatch = new CountDownLatch(1);
prep.execute(sender(), pLatch::countDown);
pLatch.await();
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() {
ParallelRadiusJob job = new ParallelRadiusJob(Integer.MAX_VALUE, service) {
@Override
public void execute(Position2 p) {
plat.injectChunkReplacement(world, p.getX(), p.getZ(), executor);
protected void execute(int x, int z) {
plat.injectChunkReplacement(world, x, z, executor);
}
@Override
public String getName() {
return "Regenerating";
}
};
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
job.queue(new Position2(i + x, j + z));
}
}
}.retarget(radius, x, z);
CountDownLatch latch = new CountDownLatch(1);
job.execute(sender(), latch::countDown);
latch.await();
int sections = mantle.getWorldHeight() >> 4;
chunkMap.forEach((pos, chunk) -> {
var c = mantle.getChunk(pos.getX(), pos.getZ()).use();
try {
c.copyFlags(chunk);
c.clear();
for (int y = 0; y < sections; y++) {
var slice = chunk.get(y);
if (slice == null) continue;
var s = c.getOrCreate(y);
slice.getSliceMap().forEach(s::putSlice);
}
} finally {
c.release();
}
});
chunkMap.forEach((pos, chunk) ->
mantle.getChunk(pos.getX(), pos.getZ()).copyFrom(chunk));
} catch (Throwable e) {
sender().sendMessage("Error while regenerating chunks");
e.printStackTrace();
@@ -339,11 +319,15 @@ public class CommandStudio implements DecreeExecutor {
O<Integer> ta = new O<>();
ta.set(-1);
var sender = sender();
var player = player();
var engine = engine();
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());
sender().sendMessage(C.GREEN + "Opened inventory!");
sender.sendMessage(C.GREEN + "Opened inventory!");
return;
}
@@ -351,7 +335,7 @@ public class CommandStudio implements DecreeExecutor {
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));
sender().sendMessage(C.GREEN + "Opening inventory now!");
@@ -704,8 +688,14 @@ public class CommandStudio implements DecreeExecutor {
}
sender().sendMessage(C.GREEN + "Sending you to the studio world!");
player().teleport(Iris.service(StudioSVC.class).getActiveProject().getActiveProvider().getTarget().getWorld().spawnLocation());
player().setGameMode(GameMode.SPECTATOR);
var player = player();
PaperLib.teleportAsync(player(), Iris.service(StudioSVC.class)
.getActiveProject()
.getActiveProvider()
.getTarget()
.getWorld()
.spawnLocation()
).thenRun(() -> player.setGameMode(GameMode.SPECTATOR));
}
@Decree(description = "Update your dimension projects VSCode workspace")
@@ -61,14 +61,14 @@ public class JigsawEditor implements Listener {
private Location target;
public JigsawEditor(Player player, IrisJigsawPiece piece, IrisObject object, File saveLocation) {
if (editors.containsKey(player)) {
editors.get(player).close();
}
if (object == null) throw new RuntimeException("Object is null! " + piece.getObject());
editors.compute(player, ($, current) -> {
if (current != null) {
current.exit();
}
return this;
});
editors.put(player, this);
if (object == null) {
throw new RuntimeException("Object is null! " + piece.getObject());
}
this.object = object;
this.player = player;
origin = player.getLocation().clone().add(0, 7, 0);
@@ -157,7 +157,7 @@ public abstract class ExternalDataProvider implements Listener {
protected static List<BlockProperty> YAW_FACE_BIOME_PROPERTIES = List.of(
BlockProperty.ofEnum(BiomeColor.class, "matchBiome", null),
BlockProperty.ofBoolean("randomYaw", false),
BlockProperty.ofFloat("yaw", 0, 0, 360f, false, true),
BlockProperty.ofDouble("yaw", 0, 0, 360f, false, true),
BlockProperty.ofBoolean("randomFace", true),
new BlockProperty(
"face",
@@ -0,0 +1,159 @@
package com.volmit.iris.core.link.data;
import com.volmit.iris.core.link.ExternalDataProvider;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.IrisCustomData;
import com.volmit.iris.util.math.RNG;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.properties.BooleanProperty;
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
public class CraftEngineDataProvider extends ExternalDataProvider {
public CraftEngineDataProvider() {
super("CraftEngine");
}
private static final BlockProperty[] FURNITURE_PROPERTIES = new BlockProperty[] {
BlockProperty.ofBoolean("randomYaw", false),
BlockProperty.ofDouble("yaw", 0, 0, 360f, false, true),
BlockProperty.ofBoolean("randomPitch", false),
BlockProperty.ofDouble("pitch", 0, 0, 360f, false, true),
};
@Override
public void init() {
}
@Override
public @NotNull List<BlockProperty> getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException {
var block = CraftEngineBlocks.byId(Key.of(blockId.namespace(), blockId.key()));
if (block != null) return block.properties()
.stream()
.map(CraftEngineDataProvider::convert)
.toList();
var furniture = CraftEngineFurniture.byId(Key.of(blockId.namespace(), blockId.key()));
if (furniture != null) {
BlockProperty[] properties = Arrays.copyOf(FURNITURE_PROPERTIES, 5);
properties[4] = new BlockProperty(
"variant",
String.class,
furniture.anyVariantName(),
furniture.variants().keySet(),
Function.identity()
);
return List.of(properties);
}
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
@Override
public @NotNull ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) throws MissingResourceException {
var item = CraftEngineItems.byId(Key.of(itemId.namespace(), itemId.key()));
if (item == null) throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key());
return item.buildItemStack();
}
@Override
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) throws MissingResourceException {
var key = Key.of(blockId.namespace(), blockId.key());
if (CraftEngineBlocks.byId(key) == null && CraftEngineFurniture.byId(key) == null)
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
return IrisCustomData.of(B.getAir(), ExternalDataSVC.buildState(blockId, state));
}
@Override
public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) {
var pair = ExternalDataSVC.parseState(blockId);
var key = Key.of(blockId.namespace(), blockId.key());
var state = pair.getB();
var customBlock = CraftEngineBlocks.byId(key);
if (customBlock != null) {
ImmutableBlockState blockState = customBlock.defaultState();
for (var entry : state.entrySet()) {
var property = customBlock.getProperty(entry.getKey());
if (property == null) continue;
var tag = property.optional(entry.getValue()).orElse(null);
if (tag == null) continue;
blockState = ImmutableBlockState.with(blockState, property, tag);
}
CraftEngineBlocks.place(block.getLocation(), blockState, false);
return;
}
var furniture = CraftEngineFurniture.byId(key);
if (furniture == null) return;
var location = parseYawAndPitch(engine, block, state);
String variant = state.getOrDefault("variant", furniture.anyVariantName());
CraftEngineFurniture.place(location, furniture, variant, false);
}
private static Location parseYawAndPitch(@NotNull Engine engine, @NotNull Block block, @NotNull Map<String, String> state) {
Location location = block.getLocation();
long seed = engine.getSeedManager().getSeed() + Cache.key(block.getX(), block.getZ()) + block.getY();
RNG rng = new RNG(seed);
if ("true".equals(state.get("randomYaw"))) {
location.setYaw(rng.f(0, 360));
} else if (state.containsKey("yaw")) {
location.setYaw(Float.parseFloat(state.get("yaw")));
}
if ("true".equals(state.get("randomPitch"))) {
location.setPitch(rng.f(0, 360));
} else if (state.containsKey("pitch")) {
location.setPitch(Float.parseFloat(state.get("pitch")));
}
return location;
}
@Override
public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) {
return (switch (dataType) {
case ENTITY -> Stream.<Key>empty();
case ITEM -> CraftEngineItems.loadedItems().keySet().stream();
case BLOCK -> Stream.concat(CraftEngineBlocks.loadedBlocks().keySet().stream(),
CraftEngineFurniture.loadedFurniture().keySet().stream());
}).map(key -> new Identifier(key.namespace(), key.value())).toList();
}
@Override
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
Key key = Key.of(id.namespace(), id.key());
return switch (dataType) {
case ENTITY -> false;
case ITEM -> CraftEngineItems.byId(key) != null;
case BLOCK -> (CraftEngineBlocks.byId(key) != null || CraftEngineFurniture.byId(key) != null);
};
}
private static <T extends Comparable<T>> BlockProperty convert(Property<T> raw) {
return switch (raw) {
case BooleanProperty property -> BlockProperty.ofBoolean(property.name(), property.defaultValue());
case IntegerProperty property -> BlockProperty.ofLong(property.name(), property.defaultValue(), property.min, property.max, false, false);
default -> new BlockProperty(raw.name(), raw.valueClass(), raw.defaultValue(), raw.possibleValues(), raw::valueName);
};
}
}
@@ -67,7 +67,7 @@ public class HMCLeavesDataProvider extends ExternalDataProvider {
BlockData blockData = Bukkit.createBlockData(material);
if (IrisSettings.get().getGenerator().preventLeafDecay && blockData instanceof Leaves leaves)
leaves.setPersistent(true);
return new IrisCustomData(blockData, ExternalDataSVC.buildState(blockId, state));
return IrisCustomData.of(blockData, ExternalDataSVC.buildState(blockId, state));
}
@NotNull
@@ -5,7 +5,6 @@ import com.volmit.iris.core.link.ExternalDataProvider;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.IrisCustomData;
import dev.lone.itemsadder.api.CustomBlock;
import dev.lone.itemsadder.api.CustomStack;
@@ -19,12 +18,13 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.stream.Collectors;
public class ItemAdderDataProvider extends ExternalDataProvider {
private final KSet<String> itemNamespaces = new KSet<>();
private final KSet<String> blockNamespaces = new KSet<>();
private volatile Set<String> itemNamespaces = Set.of();
private volatile Set<String> blockNamespaces = Set.of();
public ItemAdderDataProvider() {
super("ItemsAdder");
@@ -47,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
if (block == null) {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
return new IrisCustomData(block.getBaseBlockData(), blockId);
return IrisCustomData.of(block.getBaseBlockData(), blockId);
}
@NotNull
@@ -94,9 +94,9 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
private void updateNamespaces(DataType dataType) {
var namespaces = getTypes(dataType).stream().map(Identifier::namespace).collect(Collectors.toSet());
var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces;
currentNamespaces.removeIf(n -> !namespaces.contains(n));
currentNamespaces.addAll(namespaces);
if (dataType == DataType.ITEM) itemNamespaces = namespaces;
else blockNamespaces = namespaces;
Iris.debug("Updated ItemAdder namespaces: " + dataType + " - " + namespaces);
}
@Override
@@ -33,7 +33,7 @@ public class KGeneratorsDataProvider extends ExternalDataProvider {
@Override
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());
return new IrisCustomData(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state));
return IrisCustomData.of(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state));
}
@Override
@@ -72,7 +72,7 @@ public class MythicCrucibleDataProvider extends ExternalDataProvider {
CustomBlockItemContext blockItemContext = crucibleItem.getBlockData();
FurnitureItemContext furnitureItemContext = crucibleItem.getFurnitureData();
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) {
return blockItemContext.getBlockData();
}
@@ -49,9 +49,9 @@ public class NexoDataProvider extends ExternalDataProvider {
BlockData data = NexoBlocks.blockData(blockId.key());
if (data == null)
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())) {
return new IrisCustomData(B.getAir(), blockState);
return IrisCustomData.of(B.getAir(), blockState);
}
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.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
@Data
public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
private static final KMap<File, IrisData> dataLoaders = new KMap<>();
private final File dataFolder;
private final int id;
private final PackEnvironment environment;
private boolean closed = false;
private PackEnvironment environment;
private ResourceLoader<IrisBiome> biomeLoader;
private ResourceLoader<IrisLootTable> lootLoader;
private ResourceLoader<IrisRegion> regionLoader;
@@ -92,7 +91,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.engine = null;
this.dataFolder = dataFolder;
this.id = RNG.r.imax();
this.environment = PackEnvironment.create(this);
hotloaded();
}
@@ -105,7 +103,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
public static void dereference() {
dataLoaders.v().forEach(IrisData::cleanupEngine);
dataLoaders.values().forEach(IrisData::cleanupEngine);
}
public static int cacheSize() {
@@ -350,7 +348,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
public synchronized void hotloaded() {
closed = false;
environment.close();
possibleSnippets = new KMap<>();
builder = new GsonBuilder()
.addDeserializationExclusionStrategy(this)
@@ -382,6 +379,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.imageLoader = registerLoader(IrisImage.class);
this.scriptLoader = registerLoader(IrisScript.class);
this.matterObjectLoader = registerLoader(IrisMatterObject.class);
this.environment = PackEnvironment.create(this);
builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
gson = builder.create();
@@ -389,6 +387,10 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
.map(IrisDimension::getDataScripts)
.flatMap(KList::stream)
.forEach(environment::execute);
if (engine != null) {
engine.hotload();
}
}
public void dump() {
@@ -404,6 +406,33 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
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) {
if (f.getPath().startsWith(getDataFolder().getPath())) {
String[] full = f.getPath().split("\\Q" + File.separator + "\\E");
@@ -35,7 +35,7 @@ import java.io.File;
@Data
public abstract class IrisRegistrant {
@Desc("Preprocess this object in-memory when it's loaded, run scripts using the variable 'Iris.getPreprocessorObject()' and modify properties about this object before it's used.")
@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)
@ArrayType(min = 1, type = String.class)
private KList<String> preprocessors = new KList<>();
@@ -46,6 +46,7 @@ import lombok.ToString;
import java.io.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -170,7 +171,6 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return possibleKeys;
}
KSet<String> m = new KSet<>();
KList<File> files = getFolders();
if (files == null) {
@@ -178,6 +178,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return possibleKeys;
}
HashSet<String> m = new HashSet<>();
for (File i : files) {
for (File j : matchAllFiles(i, (f) -> f.getName().endsWith(".json"))) {
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;
}
firstAccess.add(name);
var set = firstAccess;
if (set != null) firstAccess.add(name);
return loadCache.get(name);
}
@@ -342,21 +344,24 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
}
din.close();
file.deleteOnExit();
Iris.info("Loading " + s.size() + " prefetch " + getFolderName());
firstAccess = null;
loadAllParallel(s);
}
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());
File file = Iris.instance.getDataFile("prefetch/" + id + "/" + Math.abs(getFolderName().hashCode()) + ".ipfch");
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
GZIPOutputStream gzo = new CustomOutputStream(fos, 9);
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);
}
@@ -28,9 +28,11 @@ import java.util.List;
public class INMS {
private static final Version CURRENT = Boolean.getBoolean("iris.no-version-limit") ?
new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, null) :
new Version(21, 8, null);
new Version(21, 11, null);
private static final List<Version> REVISION = List.of(
new Version(21, 11, "v1_21_R7"),
new Version(21, 9, "v1_21_R6"),
new Version(21, 6, "v1_21_R5"),
new Version(21, 5, "v1_21_R4"),
new Version(21, 4, "v1_21_R3"),
@@ -19,10 +19,8 @@
package com.volmit.iris.core.nms;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.nms.container.StructurePlacement;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
@@ -124,7 +122,7 @@ public interface INMSBinding {
Color getBiomeColor(Location location, BiomeColor type);
default DataVersion getDataVersion() {
return DataVersion.V1192;
return DataVersion.V1_19_2;
}
default int getSpawnChunkCount(World world) {
@@ -17,7 +17,7 @@ public class BlockProperty {
private final Function<Object, String> nameFunction;
private final Function<Object, Object> jsonFunction;
public <T extends Comparable<T>> BlockProperty(
public <T extends Comparable<T>> BlockProperty(
String name,
Class<T> type,
T defaultValue,
@@ -42,7 +42,7 @@ public class BlockProperty {
);
}
public static BlockProperty ofFloat(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) {
public static BlockProperty ofDouble(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) {
return new BoundedDouble(
name,
defaultValue,
@@ -54,6 +54,18 @@ public class BlockProperty {
);
}
public static BlockProperty ofLong(String name, long defaultValue, long min, long max, boolean exclusiveMin, boolean exclusiveMax) {
return new BoundedLong(
name,
defaultValue,
min,
max,
exclusiveMin,
exclusiveMax,
(l) -> Long.toString(l)
);
}
public static BlockProperty ofBoolean(String name, boolean defaultValue) {
return new BlockProperty(
name,
@@ -122,6 +134,36 @@ public class BlockProperty {
return Objects.hash(name, values, type);
}
private static class BoundedLong extends BlockProperty {
private final long min, max;
private final boolean exclusiveMin, exclusiveMax;
public BoundedLong(
String name,
long defaultValue,
long min,
long max,
boolean exclusiveMin,
boolean exclusiveMax,
Function<Long, String> nameFunction
) {
super(name, Long.class, defaultValue, List.of(), nameFunction);
this.min = min;
this.max = max;
this.exclusiveMin = exclusiveMin;
this.exclusiveMax = exclusiveMax;
}
@Override
public JSONObject buildJson() {
return super.buildJson()
.put("minimum", min)
.put("maximum", max)
.put("exclusiveMinimum", exclusiveMin)
.put("exclusiveMaximum", exclusiveMax);
}
}
private static class BoundedDouble extends BlockProperty {
private final double min, max;
private final boolean exclusiveMin, exclusiveMax;
@@ -4,6 +4,7 @@ import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.datapack.v1192.DataFixerV1192;
import com.volmit.iris.core.nms.datapack.v1206.DataFixerV1206;
import com.volmit.iris.core.nms.datapack.v1213.DataFixerV1213;
import com.volmit.iris.core.nms.datapack.v1217.DataFixerV1217;
import com.volmit.iris.util.collection.KMap;
import lombok.AccessLevel;
import lombok.Getter;
@@ -14,9 +15,10 @@ import java.util.function.Supplier;
@Getter
public enum DataVersion {
UNSUPPORTED("0.0.0", 0, () -> null),
V1192("1.19.2", 10, DataFixerV1192::new),
V1205("1.20.6", 41, DataFixerV1206::new),
V1213("1.21.3", 57, DataFixerV1213::new);
V1_19_2("1.19.2", 10, DataFixerV1192::new),
V1_20_5("1.20.6", 41, DataFixerV1206::new),
V1_21_3("1.21.3", 57, DataFixerV1213::new),
V1_21_11("1.21.11", 75, DataFixerV1217::new);
private static final KMap<DataVersion, IDataFixer> cache = new KMap<>();
@Getter(AccessLevel.NONE)
private final Supplier<IDataFixer> constructor;
@@ -0,0 +1,170 @@
package com.volmit.iris.core.nms.datapack.v1217;
import com.volmit.iris.core.nms.datapack.v1213.DataFixerV1213;
import com.volmit.iris.engine.object.IrisBiomeCustom;
import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject;
import java.util.Map;
public class DataFixerV1217 extends DataFixerV1213 {
private static final Map<Dimension, String> DIMENSIONS = Map.of(
Dimension.OVERWORLD, """
{
"ambient_light": 0.0,
"attributes": {
"minecraft:audio/ambient_sounds": {
"mood": {
"block_search_extent": 8,
"offset": 2.0,
"sound": "minecraft:ambient.cave",
"tick_delay": 6000
}
},
"minecraft:audio/background_music": {
"creative": {
"max_delay": 24000,
"min_delay": 12000,
"sound": "minecraft:music.creative"
},
"default": {
"max_delay": 24000,
"min_delay": 12000,
"sound": "minecraft:music.game"
}
},
"minecraft:visual/cloud_color": "#ccffffff",
"minecraft:visual/fog_color": "#c0d8ff",
"minecraft:visual/sky_color": "#78a7ff"
},
"timelines": "#minecraft:in_overworld"
}""",
Dimension.NETHER, """
{
"ambient_light": 0.1,
"attributes": {
"minecraft:gameplay/sky_light_level": 4.0,
"minecraft:gameplay/snow_golem_melts": true,
"minecraft:visual/fog_end_distance": 96.0,
"minecraft:visual/fog_start_distance": 10.0,
"minecraft:visual/sky_light_color": "#7a7aff",
"minecraft:visual/sky_light_factor": 0.0
},
"cardinal_light": "nether",
"skybox": "none",
"timelines": "#minecraft:in_nether"
}""",
Dimension.END, """
{
"ambient_light": 0.25,
"attributes": {
"minecraft:audio/ambient_sounds": {
"mood": {
"block_search_extent": 8,
"offset": 2.0,
"sound": "minecraft:ambient.cave",
"tick_delay": 6000
}
},
"minecraft:audio/background_music": {
"default": {
"max_delay": 24000,
"min_delay": 6000,
"replace_current_music": true,
"sound": "minecraft:music.end"
}
},
"minecraft:visual/fog_color": "#181318",
"minecraft:visual/sky_color": "#000000",
"minecraft:visual/sky_light_color": "#e580ff",
"minecraft:visual/sky_light_factor": 0.0
},
"skybox": "end",
"timelines": "#minecraft:in_end"
}"""
);
@Override
public JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
json = super.fixCustomBiome(biome, json);
var effects = json.getJSONObject("effects");
var attributes = new JSONObject();
attributes.put("minecraft:visual/fog_color", effects.remove("fog_color"));
attributes.put("minecraft:visual/sky_color", effects.remove("sky_color"));
attributes.put("minecraft:visual/water_fog_color", effects.remove("water_fog_color"));
JSONObject particle = (JSONObject) effects.remove("particle");
if (particle != null) {
particle.put("particle", particle.remove("options"));
attributes.put("minecraft:visual/ambient_particles", new JSONArray()
.put(particle));
}
json.put("attributes", attributes);
return json;
}
@Override
public void fixDimension(Dimension dimension, JSONObject json) {
super.fixDimension(dimension, json);
var attributes = new JSONObject();
if ((Boolean) json.remove("ultrawarm")) {
attributes.put("minecraft:gameplay/water_evaporates", true);
attributes.put("minecraft:gameplay/fast_lava", true);
attributes.put("minecraft:gameplay/snow_golem_melts", true);
attributes.put("minecraft:visual/default_dripstone_particle", new JSONObject()
.put("type", "minecraft:dripping_dripstone_lava"));
}
if ((Boolean) json.remove("bed_works")) {
attributes.put("minecraft:gameplay/bed_rule", new JSONObject()
.put("can_set_spawn", "always")
.put("can_sleep", "when_dark")
.put("error_message", new JSONObject()
.put("translate", "block.minecraft.bed.no_sleep")));
} else {
attributes.put("minecraft:gameplay/bed_rule", new JSONObject()
.put("can_set_spawn", "never")
.put("can_sleep", "never")
.put("explodes", true));
}
attributes.put("minecraft:gameplay/respawn_anchor_works", json.remove("respawn_anchor_works"));
attributes.put("minecraft:gameplay/piglins_zombify", json.remove("piglin_safe"));
attributes.put("minecraft:gameplay/can_start_raid", json.remove("has_raids"));
var cloud_height = json.remove("cloud_height");
if (cloud_height != null) attributes.put("minecraft:visual/cloud_height", cloud_height);
boolean natural = (Boolean) json.remove("natural");
attributes.put("minecraft:gameplay/nether_portal_spawns_piglin", natural);
if (natural != (dimension == Dimension.OVERWORLD)) {
attributes.put("minecraft:gameplay/eyeblossom_open", natural);
attributes.put("minecraft:gameplay/creaking_active", natural);
}
//json.put("has_fixed_time", json.remove("fixed_time") != null); //TODO investigate
json.put("attributes", attributes);
json.remove("effects");
var defaults = new JSONObject(DIMENSIONS.get(dimension));
merge(json, defaults);
}
private void merge(JSONObject base, JSONObject override) {
for (String key : override.keySet()) {
switch (base.opt(key)) {
case null -> base.put(key, override.opt(key));
case JSONObject base1 when override.opt(key) instanceof JSONObject override1 -> merge(base1, override1);
case JSONArray base1 when override.opt(key) instanceof JSONArray override1 -> {
for (Object o : override1) {
base1.put(o);
}
}
default -> {}
}
}
}
}
@@ -2,16 +2,15 @@ package com.volmit.iris.core.pregenerator;
import com.volmit.iris.Iris;
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.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.plugin.chunk.TicketHolder;
import com.volmit.iris.util.profile.LoadBalancer;
import com.volmit.iris.util.scheduling.J;
import io.papermc.lib.PaperLib;
@@ -21,7 +20,6 @@ import org.bukkit.World;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -31,7 +29,7 @@ public class ChunkUpdater {
private static final String REGION_PATH = "region" + File.separator + "r.";
private final AtomicBoolean paused = 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 AtomicInteger totalMaxChunks = new AtomicInteger();
private final AtomicInteger chunksProcessed = new AtomicInteger();
@@ -40,13 +38,13 @@ public class ChunkUpdater {
private final AtomicBoolean serverEmpty = new AtomicBoolean(true);
private final AtomicLong lastCpsTime = new AtomicLong(M.ms());
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 LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange);
private final AtomicLong startTime = new AtomicLong();
private final Dimensions dimensions;
private final PregenTask task;
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService chunkExecutor = IrisSettings.get().getUpdater().isNativeThreads() ? Executors.newFixedThreadPool(coreLimit) : Executors.newVirtualThreadPerTaskExecutor();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final CountDownLatch latch;
private final Engine engine;
@@ -55,6 +53,7 @@ public class ChunkUpdater {
public ChunkUpdater(World world) {
this.engine = IrisToolbelt.access(world).getEngine();
this.world = world;
this.holder = Iris.tickets.getHolder(world);
this.dimensions = calculateWorldDimensions(new File(world.getWorldFolder(), "region"));
this.task = dimensions.task();
this.totalMaxChunks.set(dimensions.count * 1024);
@@ -113,7 +112,6 @@ public class ChunkUpdater {
e.printStackTrace();
}
}, 0, 3, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(this::unloadChunks, 0, 1, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(() -> {
boolean empty = Bukkit.getOnlinePlayers().isEmpty();
if (serverEmpty.getAndSet(empty) == empty)
@@ -128,6 +126,7 @@ public class ChunkUpdater {
t.setPriority(Thread.MAX_PRIORITY);
t.start();
Iris.service(PreservationSVC.class).register(t);
} catch (Exception e) {
e.printStackTrace();
}
@@ -140,8 +139,6 @@ public class ChunkUpdater {
chunkExecutor.shutdown();
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
scheduler.shutdownNow();
unloadAndSaveAllChunks();
} catch (Exception ignored) {}
@@ -200,20 +197,16 @@ public class ChunkUpdater {
return;
}
var mc = engine.getMantle().getMantle().getChunk(x, z).use();
try {
Chunk c = world.getChunkAt(x, z);
engine.getMantle().getMantle().getChunk(c);
engine.updateChunk(c);
for (int xx = -1; xx <= 1; xx++) {
for (int zz = -1; zz <= 1; zz++) {
var counter = lastUse.get(Cache.key(x + xx, z + zz));
if (counter != null) counter.getB().decrementAndGet();
}
}
removeTickets(x, z);
} finally {
chunksUpdated.incrementAndGet();
chunksProcessed.getAndIncrement();
mc.release();
}
}
@@ -235,41 +228,16 @@ public class ChunkUpdater {
for (int dz = -1; dz <= 1; dz++) {
int xx = x + dx;
int zz = z + dz;
executor.submit(() -> {
try {
Chunk c;
try {
c = PaperLib.getChunkAtAsync(world, xx, zz, false, true)
.thenApply(chunk -> {
if (chunk != null)
chunk.addPluginChunkTicket(Iris.instance);
return chunk;
}).get();
} catch (InterruptedException | ExecutionException e) {
generated.set(false);
return;
}
if (c == null) {
generated.set(false);
return;
}
if (!c.isLoaded()) {
var future = J.sfut(() -> c.load(false));
if (future != null) future.join();
}
if (!PaperLib.isChunkGenerated(c.getWorld(), xx, zz))
generated.set(false);
var pair = lastUse.computeIfAbsent(Cache.key(c), k -> new Pair<>(0L, new AtomicInteger(-1)));
pair.setA(M.ms());
pair.getB().updateAndGet(i -> i == -1 ? 1 : ++i);
} finally {
latch.countDown();
}
});
PaperLib.getChunkAtAsync(world, xx, zz, false, true)
.thenAccept(chunk -> {
if (chunk == null || !chunk.isGenerated()) {
latch.countDown();
generated.set(false);
return;
}
holder.addTicket(chunk);
latch.countDown();
});
}
}
@@ -278,27 +246,16 @@ public class ChunkUpdater {
} catch (InterruptedException e) {
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() {
for (var key : new ArrayList<>(lastUse.keySet())) {
if (key == null) continue;
var pair = lastUse.get(key);
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);
});
private void removeTickets(int x, int z) {
for (int xx = -1; xx <= 1; xx++) {
for (int zz = -1; zz <= 1; zz++) {
holder.removeTicket(x + xx, z + zz);
}
}
}
@@ -311,7 +268,6 @@ public class ChunkUpdater {
return;
}
unloadChunks();
world.save();
}).get();
} catch (Throwable e) {
@@ -24,9 +24,11 @@ public interface PregenCache {
void write();
void trim(long unloadDuration);
static PregenCache create(File directory) {
if (directory == null) return EMPTY;
return new PregenCacheImpl(directory);
return new PregenCacheImpl(directory, 16);
}
default PregenCache sync() {
@@ -51,19 +53,16 @@ public interface PregenCache {
}
@Override
public void cacheChunk(int x, int z) {
}
public void cacheChunk(int x, int z) {}
@Override
public void cacheRegion(int x, int z) {
}
public void cacheRegion(int x, int z) {}
@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;
import lombok.AllArgsConstructor;
@AllArgsConstructor
class SynchronizedCache implements PregenCache {
private final PregenCache cache;
record SynchronizedCache(PregenCache cache) implements PregenCache {
@Override
public boolean isThreadSafe() {
return true;
@@ -45,4 +40,11 @@ class SynchronizedCache implements PregenCache {
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.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Optional;
import java.util.Scanner;
import java.util.*;
public class Gradle {
private static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
@@ -38,10 +36,15 @@ public class Gradle {
cmd[0] = gradle.getAbsolutePath();
System.arraycopy(args, 0, cmd, 1, args.length);
var process = Runtime.getRuntime().exec(cmd, ENVIRONMENT, projectDir);
attach(process.getInputStream());
attach(process.getErrorStream());
var lines = Collections.synchronizedList(new ArrayList<String>());
attach(process.getInputStream(), lines);
attach(process.getErrorStream(), lines);
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);
}
@@ -91,12 +94,12 @@ public class Gradle {
.orElseThrow(() -> new RuntimeException("Failed to find java home, please set java.home system property"));
}
private static void attach(InputStream stream) {
Thread.ofVirtual().start(() -> {
private static void attach(InputStream stream, List<String> list) {
Thread.ofPlatform().start(() -> {
try (var in = new Scanner(stream)) {
while (in.hasNextLine()) {
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.IrisRegistrant;
import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.scripting.environment.SimpleEnvironment;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.object.annotations.Snippet;
@@ -226,7 +225,7 @@ public class IrisProject {
sender.sendMessage("Can't find dimension: " + getName());
return;
} else if (sender.isPlayer()) {
sender.player().setGameMode(GameMode.SPECTATOR);
J.s(() -> sender.player().setGameMode(GameMode.SPECTATOR));
}
try {
@@ -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 {
String snipType = i.getDeclaredAnnotation(Snippet.class).value();
JSONObject o = new JSONObject();
@@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.lang.reflect.Field;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
@@ -117,49 +118,13 @@ public class SchemaBuilder {
JSONArray required = new JSONArray();
JSONArray extended = new JSONArray();
if (c.isAssignableFrom(IrisRegistrant.class) || IrisRegistrant.class.isAssignableFrom(c)) {
for (Field k : IrisRegistrant.class.getDeclaredFields()) {
k.setAccessible(true);
if (Modifier.isStatic(k.getModifiers()) || Modifier.isFinal(k.getModifiers()) || Modifier.isTransient(k.getModifiers())) {
continue;
}
JSONObject property = buildProperty(k, c);
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
var parent = c.getSuperclass();
while (parent != null && IrisRegistrant.class.isAssignableFrom(parent)) {
buildProperties(properties, required, extended, parent);
parent = parent.getSuperclass();
}
for (Field k : c.getDeclaredFields()) {
k.setAccessible(true);
if (Modifier.isStatic(k.getModifiers()) || Modifier.isFinal(k.getModifiers()) || Modifier.isTransient(k.getModifiers())) {
continue;
}
JSONObject property = buildProperty(k, c);
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
buildProperties(properties, required, extended, c);
if (required.length() > 0) {
o.put("required", required);
@@ -174,6 +139,33 @@ public class SchemaBuilder {
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) {
JSONObject prop = new JSONObject();
String type = getType(k.getType());
@@ -616,7 +608,7 @@ public class SchemaBuilder {
if (present) d.add(" ");
if (value instanceof 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)");
} else {
d.add(SYMBOL_LIMIT__N + " Default Value is " + value);
@@ -1,64 +0,0 @@
package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import io.papermc.lib.PaperLib;
import java.util.concurrent.atomic.AtomicBoolean;
public class IrisSafeguard {
private static final AtomicBoolean sfg = new AtomicBoolean(false);
public static boolean unstablemode = false;
public static boolean warningmode = false;
public static boolean stablemode = false;
public static void IrisSafeguardSystem() {
Iris.info("Enabled Iris SafeGuard");
ServerBootSFG.BootCheck();
}
public static void splash(boolean early) {
if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode))
return;
if (!sfg.getAndSet(true)) {
Iris.instance.splash();
UtilsSFG.splash();
}
}
public static String mode() {
if (unstablemode) {
return "unstable";
} else if (warningmode) {
return "warning";
} else {
return "stable";
}
}
public static void suggestPaper() {
PaperLib.suggestPaper(Iris.instance);
}
public static KMap<String, Object> asContext() {
KMap<String, Object> m = new KMap<>();
m.put("diskSpace", !ServerBootSFG.hasEnoughDiskSpace);
m.put("javaVersion", !ServerBootSFG.isCorrectJDK);
m.put("jre", ServerBootSFG.isJRE);
m.put("missingAgent", ServerBootSFG.missingAgent);
m.put("missingDimensionTypes", ServerBootSFG.missingDimensionTypes);
m.put("failedInjection", ServerBootSFG.failedInjection);
m.put("unsupportedVersion", ServerBootSFG.unsuportedversion);
m.put("serverSoftware", !ServerBootSFG.passedserversoftware);
KList<String> incompatiblePlugins = new KList<>();
ServerBootSFG.incompatibilities.forEach((plugin, present) -> {
if (present) incompatiblePlugins.add(plugin);
});
m.put("plugins", incompatiblePlugins);
return m;
}
}
@@ -1,80 +0,0 @@
package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.format.C;
public class ModesSFG {
public static void selectMode() {
if (IrisSafeguard.unstablemode) {
Iris.safeguard(C.DARK_RED + "Iris is running in Unstable Mode");
unstable();
}
if (IrisSafeguard.warningmode) {
Iris.safeguard(C.GOLD + "Iris is running in Warning Mode");
warning();
}
if (IrisSafeguard.stablemode) {
stable();
}
}
public static void stable() {
Iris.safeguard(C.BLUE + "Iris is running Stable");
}
public static void unstable() {
UtilsSFG.printIncompatibleWarnings();
if (IrisSafeguard.unstablemode) {
Iris.info("");
Iris.info(C.DARK_GRAY + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--");
Iris.info(C.RED + "Iris is running in unstable mode which may cause the following issues:");
Iris.info(C.DARK_RED + "Server Issues");
Iris.info(C.RED + "- Server won't boot");
Iris.info(C.RED + "- Data Loss");
Iris.info(C.RED + "- Unexpected behavior.");
Iris.info(C.RED + "- And More...");
Iris.info(C.DARK_RED + "World Issues");
Iris.info(C.RED + "- Worlds can't load due to corruption.");
Iris.info(C.RED + "- Worlds may slowly corrupt until they can't load.");
Iris.info(C.RED + "- World data loss.");
Iris.info(C.RED + "- And More...");
Iris.info(C.DARK_RED + "ATTENTION: " + C.RED + "While running Iris in unstable mode, you won't be eligible for support.");
Iris.info(C.DARK_RED + "CAUSE: " + C.RED + UtilsSFG.MSGIncompatibleWarnings());
if (IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode) {
Iris.info(C.DARK_RED + "Boot Unstable is set to true, continuing with the startup process.");
} else {
Iris.info(C.DARK_RED + "Go to plugins/iris/settings.json and set DoomsdayAnnihilationSelfDestructMode to true if you wish to proceed.");
while (true) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
// no
}
}
}
Iris.info("");
}
}
public static void warning() {
UtilsSFG.printIncompatibleWarnings();
if (IrisSafeguard.warningmode) {
Iris.info("");
Iris.info(C.DARK_GRAY + "--==<" + C.GOLD + " IMPORTANT " + C.DARK_GRAY + ">==--");
Iris.info(C.GOLD + "Iris is running in warning mode which may cause the following issues:");
Iris.info(C.YELLOW + "- Data Loss");
Iris.info(C.YELLOW + "- Errors");
Iris.info(C.YELLOW + "- Broken worlds");
Iris.info(C.YELLOW + "- Unexpected behavior.");
Iris.info(C.YELLOW + "- And perhaps further complications.");
Iris.info(C.GOLD + "CAUSE: " + C.YELLOW + UtilsSFG.MSGIncompatibleWarnings());
Iris.info("");
}
}
}
@@ -1,8 +0,0 @@
package com.volmit.iris.core.safeguard;
public class PerformanceSFG {
public static void calculatePerformance() {
}
}
@@ -1,194 +0,0 @@
package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KSet;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.stream.Collectors;
import static com.volmit.iris.Iris.getJavaVersion;
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
public class ServerBootSFG {
public static final Map<String, Boolean> incompatibilities = new HashMap<>();
public static boolean isCorrectJDK = true;
public static boolean hasEnoughDiskSpace = true;
public static boolean isJRE = false;
public static boolean hasPrivileges = true;
public static boolean unsuportedversion = false;
public static boolean missingDimensionTypes = false;
public static boolean missingAgent = false;
public static boolean failedInjection = false;
protected static boolean safeguardPassed;
public static boolean passedserversoftware = true;
protected static int count;
protected static byte severityLow;
protected static byte severityMedium;
protected static byte severityHigh;
public static String allIncompatibilities;
public static void BootCheck() {
Iris.info("Checking for possible conflicts..");
PluginManager pluginManager = Bukkit.getPluginManager();
Plugin[] plugins = pluginManager.getPlugins();
incompatibilities.clear();
incompatibilities.put("dynmap", false);
incompatibilities.put("Stratos", false);
String pluginName;
for (Plugin plugin : plugins) {
pluginName = plugin.getName();
Boolean flag = incompatibilities.get(pluginName);
if (flag != null && !flag) {
severityHigh++;
incompatibilities.put(pluginName, true);
}
}
StringJoiner joiner = new StringJoiner(", ");
for (Map.Entry<String, Boolean> entry : incompatibilities.entrySet()) {
if (entry.getValue()) {
joiner.add(entry.getKey());
}
}
// Legacy ServerInfo
String distro = Bukkit.getName().toLowerCase();
if (
!distro.contains("purpur") &&
!distro.contains("paper") &&
!distro.contains("spigot") &&
!distro.contains("pufferfish") &&
!distro.contains("bukkit")) {
passedserversoftware = false;
joiner.add("Server Software");
severityMedium++;
}
if (INMS.get() instanceof NMSBinding1X) {
unsuportedversion = true;
joiner.add("Unsupported Minecraft Version");
severityHigh++;
}
if (!List.of(21).contains(getJavaVersion())) {
isCorrectJDK = false;
joiner.add("Unsupported Java version");
severityMedium++;
}
if (!isJDK()) {
isJRE = true;
joiner.add("Unsupported JDK");
severityMedium++;
}
// if (!hasPrivileges()){
// hasPrivileges = false;
// joiner.add("Insufficient Privileges");
// severityMedium++;
// } Some servers dont like this
if (!enoughDiskSpace()){
hasEnoughDiskSpace = false;
joiner.add("Insufficient Disk Space");
severityMedium++;
}
if (!Agent.install()) {
missingAgent = true;
joiner.add("Missing Java Agent");
severityHigh++;
} else {
if (missingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
severityHigh++;
}
if (!INMS.get().injectBukkit()) {
failedInjection = true;
joiner.add("Failed Bukkit Injection");
severityHigh++;
}
}
allIncompatibilities = joiner.toString();
safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0);
count = severityHigh + severityMedium + severityLow;
if (safeguardPassed) {
stablemode = true;
Iris.safeguard("Stable mode has been activated.");
}
if (!safeguardPassed) {
if (severityMedium >= 1 && severityHigh == 0) {
warningmode = true;
Iris.safeguard("Warning mode has been activated.");
}
if (severityHigh >= 1) {
unstablemode = true;
Iris.safeguard("Unstable mode has been activated.");
}
}
}
public static boolean isJDK() {
try {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// If the compiler is null, it means this is a JRE environment, not a JDK.
return compiler != null;
} catch (Exception ignored) {}
return false;
}
public static boolean hasPrivileges() {
Path pv = Paths.get(Bukkit.getWorldContainer() + "iristest.json");
try (FileChannel fc = FileChannel.open(pv, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
if (Files.isReadable(pv) && Files.isWritable(pv)) {
return true;
}
} catch (Exception e) {
return false;
}
return false;
}
public static boolean enoughDiskSpace() {
File freeSpace = Bukkit.getWorldContainer();
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
return gigabytes > 3;
}
private static boolean checkJavac(String path) {
return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists());
}
private static boolean missingDimensionTypes() {
return INMS.get().missingDimensionTypes(getDimensionTypes().toArray(String[]::new));
}
private static KSet<String> getDimensionTypes() {
return IrisWorlds.get()
.getDimensions()
.map(IrisDimension::getDimensionTypeKey)
.collect(Collectors.toCollection(KSet::new));
}
}
@@ -1,76 +0,0 @@
package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.format.C;
public class UtilsSFG {
public static void splash() {
ModesSFG.selectMode();
}
public static void printIncompatibleWarnings() {
String[] parts = Iris.instance.getDescription().getVersion().split("-");
String minVersion = parts[1];
String maxVersion = parts[2];
if (ServerBootSFG.safeguardPassed) {
Iris.safeguard(C.BLUE + "0 Conflicts found");
} else {
if (IrisSafeguard.unstablemode) {
Iris.safeguard(C.DARK_RED + "" + ServerBootSFG.count + " Conflicts found");
}
if (IrisSafeguard.warningmode) {
Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found");
}
if (ServerBootSFG.incompatibilities.get("dynmap")) {
Iris.safeguard(C.RED + "Dynmap");
Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server.");
Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap.");
}
if (ServerBootSFG.incompatibilities.get("Stratos")) {
Iris.safeguard(C.YELLOW + "Stratos");
Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins.");
}
if (ServerBootSFG.unsuportedversion) {
Iris.safeguard(C.RED + "Server Version");
Iris.safeguard(C.RED + "- Iris only supports " + minVersion + " > " + maxVersion);
}
if (ServerBootSFG.missingDimensionTypes) {
Iris.safeguard(C.RED + "Dimension Types");
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
}
if (ServerBootSFG.missingAgent) {
Iris.safeguard(C.RED + "Java Agent");
Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments.");
Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath());
}
if (!ServerBootSFG.passedserversoftware) {
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");
}
if (!ServerBootSFG.hasPrivileges) {
Iris.safeguard(C.YELLOW + "Insufficient Privileges");
Iris.safeguard(C.YELLOW + "- The server has insufficient Privileges to run iris. Please contact support.");
}
if (!ServerBootSFG.hasEnoughDiskSpace) {
Iris.safeguard(C.YELLOW + "Insufficient Disk Space");
Iris.safeguard(C.YELLOW + "- The server has insufficient Free DiskSpace to run iris required 3GB+.");
}
if (!ServerBootSFG.isCorrectJDK) {
Iris.safeguard(C.YELLOW + "Unsupported java version");
Iris.safeguard(C.YELLOW + "- Please consider using JDK 21 Instead of JDK " + Iris.getJavaVersion());
}
if (ServerBootSFG.isJRE) {
Iris.safeguard(C.YELLOW + "Unsupported Server JDK");
Iris.safeguard(C.YELLOW + "- Please consider using JDK 21 Instead of JRE " + Iris.getJavaVersion());
}
}
}
public static String MSGIncompatibleWarnings() {
return ServerBootSFG.allIncompatibilities;
}
}
@@ -1,9 +1,12 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.scripting.func.UpdateExecutor;
import com.volmit.iris.core.scripting.kotlin.environment.IrisExecutionEnvironment;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.mantle.MantleChunk;
import lombok.NonNull;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
@@ -22,4 +25,6 @@ public interface EngineEnvironment extends PackEnvironment {
void postSpawnMob(@NonNull String script, @NonNull Location location, @NonNull Entity mob);
void preprocessObject(@NonNull String script, @NonNull IrisRegistrant object);
void updateChunk(@NonNull String script, @NonNull MantleChunk mantleChunk, @NonNull Chunk chunk, @NonNull UpdateExecutor executor);
}
@@ -2,6 +2,7 @@ package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.math.RNG;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;
@@ -16,4 +17,6 @@ public interface PackEnvironment extends SimpleEnvironment {
@Nullable
Object createNoise(@NonNull String script, @NonNull RNG rng);
EngineEnvironment with(@NonNull Engine engine);
}
@@ -27,8 +27,4 @@ public interface SimpleEnvironment {
@Nullable
Object evaluate(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
default void close() {
}
}
@@ -0,0 +1,22 @@
package com.volmit.iris.core.scripting.func;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface UpdateExecutor {
@NotNull Runnable wrap(int delay, @NotNull Runnable runnable);
@NotNull
default Runnable wrap(@NotNull Runnable runnable) {
return wrap(1, runnable);
}
default void execute(@NotNull Runnable runnable) {
execute(1, runnable);
}
default void execute(int delay, @NotNull Runnable runnable) {
wrap(delay, runnable).run();
}
}
@@ -1,13 +1,11 @@
package com.volmit.iris.core.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.cache.PregenCache;
import com.volmit.iris.core.tools.IrisToolbelt;
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.scheduling.Looper;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.World;
@@ -19,21 +17,33 @@ import org.bukkit.event.world.WorldUnloadEvent;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.function.Function;
public class GlobalCacheSVC implements IrisService {
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder()
.executor(KCache.EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.weakValues()
.build();
private static final KMap<String, Reference<PregenCache>> REFERENCE_CACHE = new KMap<>();
private final KMap<String, PregenCache> globalCache = new KMap<>();
private transient boolean lastState;
private static boolean disabled = true;
private Looper trimmer;
@Override
public void onEnable() {
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();
if (lastState) return;
Bukkit.getWorlds().forEach(this::createCache);
@@ -42,6 +52,9 @@ public class GlobalCacheSVC implements IrisService {
@Override
public void onDisable() {
disabled = true;
try {
trimmer.join();
} catch (InterruptedException ignored) {}
globalCache.qclear((world, cache) -> cache.write());
}
@@ -76,6 +89,7 @@ public class GlobalCacheSVC implements IrisService {
}
private void createCache(World world) {
if (!IrisToolbelt.isIrisWorld(world)) return;
globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault);
}
@@ -99,7 +113,15 @@ public class GlobalCacheSVC implements IrisService {
@NonNull
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
@@ -3,7 +3,7 @@ package com.volmit.iris.core.service;
import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris;
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.engine.framework.Engine;
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.VolmitSender;
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 org.bukkit.Bukkit;
import org.bukkit.World;
@@ -27,6 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class IrisEngineSVC implements IrisService {
private static final int TRIM_PERIOD = 2_000;
private final AtomicInteger tectonicLimit = new AtomicInteger(30);
private final AtomicInteger tectonicPlates = new AtomicInteger();
private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
@@ -64,10 +67,26 @@ public class IrisEngineSVC implements IrisService {
}
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 + "Status:");
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 + "- Period: " + C.LIGHT_PURPLE + Form.duration(TRIM_PERIOD));
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 + "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 + "- 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 + "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 + "- Iris Worlds: " + C.LIGHT_PURPLE + totalWorlds.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 + "-------------------------");
}
@@ -113,12 +136,12 @@ public class IrisEngineSVC implements IrisService {
@Override
protected long loop() {
try {
queuedTectonicPlates.set(0);
tectonicPlates.set(0);
loadedChunks.set(0);
unloaderAlive.set(0);
trimmerAlive.set(0);
totalWorlds.set(0);
int queuedPlates = 0;
int totalPlates = 0;
long chunks = 0;
int unloaders = 0;
int trimmers = 0;
int iris = 0;
double maxDuration = Long.MIN_VALUE;
double minDuration = Long.MAX_VALUE;
@@ -126,23 +149,30 @@ public class IrisEngineSVC implements IrisService {
var registered = entry.getValue();
if (registered.closed) continue;
totalWorlds.incrementAndGet();
unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0);
trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0);
iris++;
if (registered.unloaderAlive()) unloaders++;
if (registered.trimmerAlive()) trimmers++;
var engine = registered.getEngine();
if (engine == null) continue;
queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount());
tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
loadedChunks.addAndGet(entry.getKey().getLoadedChunks().length);
queuedPlates += engine.getMantle().getUnloadRegionCount();
totalPlates += engine.getMantle().getLoadedRegionCount();
chunks += entry.getKey().getLoadedChunks().length;
double duration = engine.getMantle().getAdjustedIdleDuration();
if (duration > maxDuration) maxDuration = duration;
if (duration < minDuration) minDuration = duration;
}
trimmerAlive.set(trimmers);
unloaderAlive.set(unloaders);
tectonicPlates.set(totalPlates);
queuedTectonicPlates.set(queuedPlates);
maxIdleDuration.set(maxDuration);
minIdleDuration.set(minDuration);
loadedChunks.set(chunks);
totalWorlds.set(iris);
worlds.values().forEach(Registered::update);
} catch (Throwable e) {
@@ -157,7 +187,7 @@ public class IrisEngineSVC implements IrisService {
private final class Registered {
private final String name;
private final PlatformChunkGenerator access;
private final int offset = RNG.r.nextInt(1000);
private final int offset = RNG.r.nextInt(TRIM_PERIOD);
private transient ScheduledFuture<?> trimmer;
private transient ScheduledFuture<?> unloader;
private transient boolean closed;
@@ -194,7 +224,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to trim for " + name);
e.printStackTrace();
}
}, offset, 2000, TimeUnit.MILLISECONDS);
}, offset, TRIM_PERIOD, TimeUnit.MILLISECONDS);
}
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
@@ -214,7 +244,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to unload for " + name);
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.data.KCache;
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.scheduling.Looper;
import org.jetbrains.annotations.Unmodifiable;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PreservationSVC implements IrisService {
private final List<Thread> threads = 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;
public void register(Thread t) {
@@ -48,22 +51,18 @@ public class PreservationSVC implements IrisService {
}
public void printCaches() {
long s = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getSize).sum();
long m = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getMaxSize).sum();
var c = getCaches();
long s = 0;
long m = 0;
double p = 0;
double mf = 0;
double mf = Math.max(c.size(), 1);
for (MeteredCache i : caches) {
if (i.isClosed()) {
continue;
}
mf++;
for (MeteredCache i : c) {
s += i.getSize();
m += i.getMaxSize();
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");
}
@@ -119,14 +118,29 @@ public class PreservationSVC implements IrisService {
}
public void updateCaches() {
caches.removeIf(MeteredCache::isClosed);
caches.removeIf(ref -> {
var c = ref.get();
return c == null || c.isClosed();
});
}
public void registerCache(MeteredCache cache) {
caches.add(cache);
caches.add(new WeakReference<>(cache));
}
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
public Engine getEngine() {
return engine;
@@ -225,7 +235,7 @@ public class TreeSVC implements IrisService {
if (d instanceof IrisCustomData data) {
block.setBlockData(data.getBase(), false);
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();
var latch = new CountDownLatch(1);
var holder = Iris.tickets.getHolder(p.getWorld());
new Job() {
private int i;
private Chunk chunk;
@@ -108,7 +109,7 @@ public class WandSVC implements IrisService {
while (time > M.ms()) {
if (!it.hasNext()) {
if (chunk != null) {
chunk.removePluginChunkTicket(Iris.instance);
holder.removeTicket(chunk);
chunk = null;
}
@@ -122,9 +123,10 @@ public class WandSVC implements IrisService {
var bChunk = b.getChunk();
if (chunk == null) {
chunk = bChunk;
chunk.addPluginChunkTicket(Iris.instance);
holder.addTicket(chunk);
} else if (chunk != bChunk) {
chunk.removePluginChunkTicket(Iris.instance);
holder.removeTicket(chunk);
holder.addTicket(bChunk);
chunk = bChunk;
}
@@ -2,29 +2,21 @@ package com.volmit.iris.core.tools;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.nbt.io.NBTUtil;
import com.volmit.iris.util.nbt.io.NamedTag;
import com.volmit.iris.util.nbt.tag.*;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.reflect.V;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.apache.commons.io.FileUtils;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.FileUtil;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.io.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
@@ -39,13 +31,17 @@ public class IrisConverter {
sender.sendMessage("No schematic files to convert found in " + folder.getAbsolutePath());
return;
}
AtomicInteger counter = new AtomicInteger(0);
var stopwatch = PrecisionStopwatch.start();
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> {
for (File schem : fileList) {
try {
for (File schem : fileList) {
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
IrisObject object;
boolean largeObject = false;
NamedTag tag = null;
NamedTag tag;
try {
tag = NBTUtil.read(schem);
} catch (IOException e) {
@@ -53,17 +49,20 @@ public class IrisConverter {
throw new RuntimeException(e);
}
CompoundTag compound = (CompoundTag) tag.getTag();
int version = resolveVersion(compound);
if (!(version == 2 || version == 3))
throw new RuntimeException(C.RED + "Unsupported schematic version: " + version);
if (compound.containsKey("Palette") && compound.containsKey("Width") && compound.containsKey("Height") && compound.containsKey("Length")) {
compound = version == 3 ? (CompoundTag) compound.get("Schematic") : compound;
int objW = ((ShortTag) compound.get("Width")).getValue();
int objH = ((ShortTag) compound.get("Height")).getValue();
int objD = ((ShortTag) compound.get("Length")).getValue();
int i = -1;
int mv = objW * objH * objD;
AtomicInteger v = new AtomicInteger(0);
if (mv > 500_000) {
if (mv > 2_000_000) {
largeObject = true;
Iris.info(C.GRAY + "Converting.. "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
Iris.info(C.GRAY + "Converting.. " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
Iris.info(C.GRAY + "- It may take a while");
if (sender.isPlayer()) {
i = J.ar(() -> {
@@ -72,6 +71,7 @@ public class IrisConverter {
}
}
compound = version == 3 ? (CompoundTag) compound.get("Blocks") : compound;
CompoundTag paletteTag = (CompoundTag) compound.get("Palette");
Map<Integer, BlockData> blockmap = new HashMap<>(paletteTag.size(), 0.9f);
for (Map.Entry<String, Tag<?>> entry : paletteTag.getValue().entrySet()) {
@@ -82,14 +82,16 @@ public class IrisConverter {
blockmap.put(blockId, bd);
}
ByteArrayTag byteArray = (ByteArrayTag) compound.get("BlockData");
boolean isBytes = version == 3 ? compound.getByteArrayTag("Data").length() < 128 : ((IntTag) compound.get("PaletteMax")).getValue() < 128;
ByteArrayTag byteArray = version == 3 ? (ByteArrayTag) compound.get("Data") : (ByteArrayTag) compound.get("BlockData");
byte[] originalBlockArray = byteArray.getValue();
IrisObject object = new IrisObject(objW, objH, objD);
var din = new DataInputStream(new ByteArrayInputStream(originalBlockArray));
object = new IrisObject(objW, objH, objD);
for (int h = 0; h < objH; h++) {
for (int d = 0; d < objD; d++) {
for (int w = 0; w < objW; w++) {
BlockData bd = blockmap.get((int) originalBlockArray[v.get()]);
int blockIndex = isBytes ? din.read() & 0xFF : Varint.readUnsignedVarInt(din);
BlockData bd = blockmap.get(blockIndex);
if (!bd.getMaterial().isAir()) {
object.setUnsigned(w, h, d, bd);
}
@@ -97,42 +99,59 @@ public class IrisConverter {
}
}
}
if (i != -1) J.car(i);
try {
object.shrinkwrap();
object.write(new File(folder, schem.getName().replace(".schem", ".iob")));
} catch (IOException e) {
Iris.info(C.RED + "Failed to save: " + schem.getName());
throw new RuntimeException(e);
}
if (sender.isPlayer()) {
if (largeObject) {
sender.sendMessage(C.IRIS + "Converted "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
} else {
sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
counter.incrementAndGet();
if (sender.isPlayer()) {
if (largeObject) {
sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
} else {
sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
}
}
if (largeObject) {
Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
} else {
Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
}
FileUtils.delete(schem);
} catch (IOException e) {
sender.sendMessage(C.RED + "Failed to save: " + schem.getName());
throw new IOException(e);
}
if (largeObject) {
Iris.info(C.GRAY + "Converted "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
} else {
Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
}
FileUtils.delete(schem);
}
} catch (Exception e) {
Iris.info(C.RED + "Failed to convert: " + schem.getName());
if (sender.isPlayer()) {
} catch (Exception e) {
sender.sendMessage(C.RED + "Failed to convert: " + schem.getName());
e.printStackTrace();
}
e.printStackTrace();
Iris.reportError(e);
}
}
sender.sendMessage(C.GRAY + "converted: " + fileList.length);
stopwatch.end();
if (counter.get() != 0) {
sender.sendMessage(C.GRAY + "Converted: " + counter.get() + " in " + Form.duration(stopwatch.getMillis()));
}
if (counter.get() < fileList.length) {
sender.sendMessage(C.RED + "Some schematics failed to convert. Check the console for details.");
}
});
}
private static int resolveVersion(CompoundTag compound) throws Exception {
try {
IntTag root = compound.getIntTag("Version");
if (root != null) {
return root.getValue();
}
CompoundTag schematic = (CompoundTag) compound.get("Schematic");
return schematic.getIntTag("Version").getValue();
} catch (NullPointerException e) {
throw new Exception("Cannot resolve schematic version", e);
}
}
}
@@ -34,6 +34,7 @@ import com.volmit.iris.util.plugin.VolmitSender;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import java.io.File;
import java.io.IOException;
@@ -45,6 +46,7 @@ import java.util.Map;
* Hope you packed snacks & road sodas.
*/
public class IrisToolbelt {
@ApiStatus.Internal
public static Map<String, Boolean> toolbeltConfiguration = new HashMap<>();
/**
@@ -232,7 +234,11 @@ public class IrisToolbelt {
}
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) {
@@ -89,7 +89,7 @@ public class IrisComplex implements DataProvider {
}
public IrisComplex(Engine engine, boolean simple) {
int cacheSize = IrisSettings.get().getPerformance().getCacheSize();
int cacheSize = IrisSettings.get().getPerformance().getNoiseCacheSize();
IrisBiome emptyBiome = new IrisBiome();
UUID focusUUID = UUID.nameUUIDFromBytes("focus".getBytes());
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 -> {
int radius = components.stream()
.filter(MantleComponent::isEnabled)
.mapToInt(MantleComponent::getRadius)
.max()
.orElse(0);
return new Pair<>(List.copyOf(components), radius);
})
.filter(pair -> !pair.getA().isEmpty())
.toList();
int radius = 0;
@@ -23,6 +23,7 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineAssignedWorldManager;
import com.volmit.iris.engine.object.*;
@@ -57,13 +58,10 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -81,6 +79,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private final ChronoLatch cln;
private final ChronoLatch chunkUpdater;
private final ChronoLatch chunkDiscovery;
private final KMap<Long, Future<?>> cleanup = new KMap<>();
private final ScheduledExecutorService cleanupService;
private double energy = 25;
private int entityCount = 0;
private long charge = 0;
@@ -98,6 +98,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
looper = null;
chunkUpdater = null;
chunkDiscovery = null;
cleanupService = null;
id = -1;
}
@@ -109,6 +110,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
cl = new ChronoLatch(3000);
ecl = new ChronoLatch(250);
clw = new ChronoLatch(1000, true);
cleanupService = Executors.newSingleThreadScheduledExecutor(runnable -> {
var thread = new Thread(runnable, "Iris Mantle Cleanup " + getTarget().getWorld().name());
thread.setPriority(Thread.MIN_PRIORITY);
return thread;
});
id = engine.getCacheID();
energy = 25;
looper = new Looper() {
@@ -122,10 +128,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
getEngine().getWorld().tryGetRealWorld();
}
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem() && !IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()) {
return 3000;
}
if (getEngine().getWorld().hasRealWorld()) {
if (getEngine().getWorld().getPlayers().isEmpty()) {
return 5000;
@@ -139,6 +141,13 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
discoverChunks();
}
if (cln.flip()) {
engine.getEngineData().cleanup(getEngine());
}
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem() && !IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()) {
return 3000;
}
if (getDimension().isInfiniteEnergy()) {
energy += 1000;
@@ -150,10 +159,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
fixEnergy();
}
if (cln.flip()) {
engine.getEngineData().cleanup(getEngine());
}
if (precount != null) {
entityCount = 0;
for (Entity i : precount) {
@@ -181,7 +186,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
};
looper.setPriority(Thread.MIN_PRIORITY);
looper.setName("Iris World Manager");
looper.setName("Iris World Manager " + getTarget().getWorld().name());
looper.start();
}
@@ -425,22 +430,20 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
return;
}
var ref = new WeakReference<>(e.getWorld());
int cX = e.getX(), cZ = e.getZ();
J.s(() -> {
World world = ref.get();
if (world == null || !world.isChunkLoaded(cX, cZ))
return;
Long key = Cache.key(e);
cleanup.put(key, cleanupService.schedule(() -> {
cleanup.remove(key);
energy += 0.3;
fixEnergy();
getEngine().cleanupMantleChunk(cX, cZ);
}, IrisSettings.get().getPerformance().mantleCleanupDelay);
}, Math.max(IrisSettings.get().getPerformance().mantleCleanupDelay * 50L, 0), TimeUnit.MILLISECONDS));
if (generated) {
//INMS.get().injectBiomesFromMantle(e, getMantle());
if (!IrisSettings.get().getGenerator().earlyCustomBlocks) return;
e.addPluginChunkTicket(Iris.instance);
Iris.tickets.addTicket(e);
J.s(() -> {
var chunk = getMantle().getChunk(e).use();
int minY = getTarget().getWorld().minHeight();
@@ -452,12 +455,20 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
});
} finally {
chunk.release();
e.removePluginChunkTicket(Iris.instance);
Iris.tickets.removeTicket(e);
}
}, RNG.r.i(20, 60));
}
}
@Override
public void onChunkUnload(Chunk e) {
final var future = cleanup.remove(Cache.key(e));
if (future != null) {
future.cancel(false);
}
}
private void spawn(IrisPosition block, IrisSpawner spawner, boolean initial) {
if (getEngine().isClosed()) {
return;
@@ -18,6 +18,7 @@
package com.volmit.iris.engine.framework;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.math.RNG;
import org.bukkit.Chunk;
import org.bukkit.block.data.BlockData;
@@ -28,5 +29,5 @@ public interface BlockUpdater {
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.function.Function2;
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.math.BlockPosition;
import com.volmit.iris.util.math.M;
@@ -294,8 +295,8 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
var chunk = mantle.getChunk(c).use();
try {
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
Semaphore semaphore = new Semaphore(1024);
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> {
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
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++) {
if (grid[x][z] == Integer.MIN_VALUE)
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) -> {
int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) {
update(x, y, z, c, rng);
update(x, y, z, c, chunk, rng);
}
});
chunk.deleteSlices(MatterUpdate.class);
@@ -355,8 +356,18 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}, RNG.r.i(1, 20))); //Why is there a random delay here?
});
chunk.raiseFlagUnchecked(MantleFlag.SCRIPT, () -> {
var scripts = getDimension().getChunkUpdateScripts();
if (scripts == null || scripts.isEmpty())
return;
for (var script : scripts) {
getExecution().updateChunk(script, chunk, c, (delay, task) -> run(semaphore, task, delay));
}
});
try {
semaphore.acquire(3);
semaphore.acquire(1024);
} catch (InterruptedException ignored) {}
} finally {
chunk.release();
@@ -365,8 +376,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
private static Runnable run(Semaphore semaphore, Runnable runnable, int delay) {
return () -> {
if (!semaphore.tryAcquire())
return;
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
J.s(() -> {
try {
@@ -381,7 +395,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@BlockCoordinates
@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);
BlockData data = block.getBlockData();
blockUpdatedMetric();
@@ -394,17 +408,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
if (slot != null) {
KList<IrisLootTable> tables = getLootTables(rx, block);
KList<IrisLootTable> tables = getLootTables(rx, block, mc);
try {
Bukkit.getPluginManager().callEvent(new IrisLootEvent(this, block, slot, tables));
if (!tables.isEmpty()){
Iris.debug("IrisLootEvent has been accessed");
}
if (tables.isEmpty())
return;
if (tables.isEmpty()) return;
InventoryHolder m = (InventoryHolder) block.getState();
addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15);
@@ -470,13 +478,23 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@BlockCoordinates
@Override
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 rz = b.getZ();
int ry = b.getY() - getWorld().minHeight();
double he = getComplex().getHeightStream().get(rx, rz);
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 (B.isStorageChest(b.getBlockData())) {
IrisLootTable table = po.getPlacement().getTable(b.getBlockData(), getData());
@@ -799,7 +817,16 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
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()) {
return null;
}
@@ -809,7 +836,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
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) {
IrisJigsawPiece piece = container.load(getData());
if (piece.getObject().equals(object))
@@ -824,7 +851,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
}
IrisBiome biome = getBiome(x, y, z);
IrisBiome biome = getSurfaceBiome(x, z);
for (IrisObjectPlacement i : biome.getObjects()) {
if (i.getPlace().contains(object)) {
@@ -973,7 +1000,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
default void cleanupMantleChunk(int x, int z) {
if (IrisSettings.get().getPerformance().isTrimMantleInStudio() || !isStudio()) {
J.a(() -> getMantle().cleanupChunk(x, z));
getMantle().cleanupChunk(x, z);
}
}
}
@@ -34,6 +34,7 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.inventory.EquipmentSlot;
@@ -125,6 +126,13 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
}
}
@EventHandler
public void on(ChunkUnloadEvent e) {
if (e.getChunk().getWorld().equals(getTarget().getWorld().realWorld())) {
onChunkUnload(e.getChunk());
}
}
@Override
public void close() {
super.close();
@@ -45,6 +45,8 @@ public interface EngineWorldManager {
void onChunkLoad(Chunk e, boolean generated);
void onChunkUnload(Chunk e);
void chargeEnergy();
void teleportAsync(PlayerTeleportEvent e);
@@ -24,7 +24,6 @@ import com.volmit.iris.engine.object.IObjectPlacer;
import com.volmit.iris.engine.object.IrisObjectPlacement;
import com.volmit.iris.engine.object.TileData;
import com.volmit.iris.util.math.RNG;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
public class HeightmapObjectPlacer implements IObjectPlacer {
@@ -83,6 +82,16 @@ public class HeightmapObjectPlacer implements IObjectPlacer {
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
public Engine getEngine() {
return null;
@@ -59,6 +59,11 @@ public class WorldObjectPlacer implements IObjectPlacer {
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) {
RNG rx = new RNG(Cache.key(x, z));
KList<IrisLootTable> tables = engine.getLootTables(rx, block);
@@ -78,12 +83,6 @@ public class WorldObjectPlacer implements IObjectPlacer {
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
@@ -125,4 +124,13 @@ public class WorldObjectPlacer implements IObjectPlacer {
public void setTile(int xx, int yy, int zz, TileData tile) {
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;
}
}
@@ -69,7 +69,7 @@ public class PlannedPiece {
this.setRotation(rot);
this.ogObject = data.getObjectLoader().load(piece.getObject());
this.object = structure.rotated(piece, rotation);
this.piece = rotation.rotateCopy(piece);
this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset()));
this.piece.setLoadKey(piece.getLoadKey());
this.object.setLoadKey(piece.getObject());
this.ogObject.setLoadKey(piece.getObject());
@@ -154,9 +154,9 @@ public class PlannedStructure {
JigsawStructureContainer structure = JigsawStructureContainer.toContainer(getStructure());
i.setRealPositions(xx, height, zz, placer);
return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
e.set(b.getX(), b.getY(), b.getZ(), structure);
e.set(b.getX(), b.getY(), b.getZ(), piece);
placer.setData(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
placer.setData(b.getX(), b.getY(), b.getZ(), structure);
placer.setData(b.getX(), b.getY(), b.getZ(), piece);
}, 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.IrisPosition;
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.documentation.BlockCoordinates;
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.MantleChunk;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.matter.*;
import com.volmit.iris.util.matter.slices.UpdateMatter;
import com.volmit.iris.util.parallel.MultiBurst;
@@ -49,10 +46,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.volmit.iris.util.parallel.StreamUtils.forEach;
import static com.volmit.iris.util.parallel.StreamUtils.streamRadius;
public interface EngineMantle {
public interface EngineMantle extends MatterGenerator {
BlockData AIR = B.get("AIR");
Mantle getMantle();
@@ -180,49 +174,6 @@ public interface EngineMantle {
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
default <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> blocks, boolean multicore) {
if (!getEngine().getDimension().isUseMantle()) {
@@ -262,9 +213,6 @@ public interface EngineMantle {
default int getLoadedRegionCount() {
return getMantle().getLoadedRegionCount();
}
default long getLastUseMapMemoryUsage(){
return getMantle().LastUseMapMemoryUsage();
}
MantleJigsawComponent getJigsawComponent();
@@ -290,7 +238,7 @@ public interface EngineMantle {
if (!isCovered(x, z)) return;
MantleChunk chunk = getMantle().getChunk(x, z).use();
try {
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> {
chunk.deleteSlices(BlockData.class);
chunk.deleteSlices(String.class);
chunk.deleteSlices(MatterCavern.class);
@@ -301,7 +249,7 @@ public interface EngineMantle {
}
}
default long getUnloadRegionCount() {
default int getUnloadRegionCount() {
return getMantle().getUnloadRegionCount();
}
@@ -40,13 +40,12 @@ import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterCavern;
import com.volmit.iris.util.matter.TileWrapper;
import com.volmit.iris.util.noise.CNG;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Data;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
@@ -54,25 +53,31 @@ import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
public class MantleWriter implements IObjectPlacer, AutoCloseable {
private final EngineMantle engineMantle;
private final Mantle mantle;
private final KMap<Long, MantleChunk> cachedChunks;
private final Map<Long, MantleChunk> cachedChunks;
private final int radius;
private final int x;
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.mantle = mantle;
this.cachedChunks = new KMap<>();
this.radius = radius;
this.radius = radius * 2;
final int d = this.radius + 1;
this.cachedChunks = multicore ? new KMap<>(d * d, 0.75f, Math.max(32, Runtime.getRuntime().availableProcessors() * 4)) : new Long2ObjectOpenHashMap<>(d * d);
this.x = x;
this.z = z;
int r = radius / 4;
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= r; j++) {
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
}
}
final int parallelism = multicore ? Runtime.getRuntime().availableProcessors() / 2 : 4;
final var map = multicore ? cachedChunks : new KMap<Long, MantleChunk>(d * d, 1f, parallelism);
mantle.getChunks(
x - radius,
x + radius,
z - radius,
z + radius,
parallelism,
(i, j, c) -> map.put(Cache.key(i, j), c.use())
);
if (!multicore) cachedChunks.putAll(map);
}
private static Set<IrisPosition> getBallooned(Set<IrisPosition> vset, double radius) {
@@ -182,8 +187,13 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
return null;
}
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
if (chunk == null) Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
final Long key = Cache.key(cx, cz);
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;
}
@@ -35,7 +35,6 @@ import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.noise.CNG;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -45,7 +44,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, ReservedFlag.JIGSAW, 2);
super(engineMantle, ReservedFlag.JIGSAW, 1);
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)
continue;
for (BlockVector j : clump.getBlocks().keySet()) {
for (BlockVector j : clump.getBlocks().keys()) {
int nx = j.getBlockX() + x;
int ny = j.getBlockY() + y;
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.engine.framework.Engine;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.Nullable;
public interface IObjectPlacer {
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);
<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();
}
@@ -122,6 +122,10 @@ public class IrisCompat {
private static KList<IrisCompatabilityBlockFilter> getDefaultBlockCompatabilityFilters() {
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
filters.add(new IrisCompatabilityBlockFilter("WEEPING_VINES", "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("CHISELED_NETHER_BRICKS", "NETHER_BRICKS"));
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("BLACKSTONE", "COBBLESTONE"));
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_SAPLING", "BIRCH_SAPLING"));
filters.add(new IrisCompatabilityBlockFilter("POTTED_BAMBOO", "POTTED_BIRCH_SAPLING"));
filters.add(new IrisCompatabilityBlockFilter("GRASS", "SHORT_GRASS"));
return filters;
}
@@ -249,14 +249,18 @@ public class IrisDimension extends IrisRegistrant {
@Desc("A list of globally applied pre-processors")
@ArrayType(type = IrisPreProcessors.class)
private KList<IrisPreProcessors> globalPreProcessors = new KList<>();
@Desc("A list of scripts executed on engine setup")
@Desc("A list of scripts executed on engine setup\nFile extension: .engine.kts")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> engineScripts = new KList<>();
@Desc("A list of scripts executed on data setup")
@Desc("A list of scripts executed on data setup\nFile extension: .data.kts")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> dataScripts = new KList<>();
@Desc("A list of scripts executed on chunk update\nFile extension: .update.kts")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> chunkUpdateScripts = new KList<>();
@Desc("Use legacy rarity instead of modern one\nWARNING: Changing this may break expressions and image maps")
private boolean legacyRarity = true;
@@ -39,7 +39,7 @@ public class IrisDimensionMode {
private IrisDimensionModeType type = IrisDimensionModeType.OVERWORLD;
@RegistryListResource(IrisScript.class)
@Desc("The script to create the dimension mode instead of using provided types")
@Desc("The script to create the dimension mode instead of using provided types\nFile extension: .engine.kts")
private String script;
public EngineMode create(Engine engine) {
@@ -166,12 +166,12 @@ public class IrisEntity extends IrisRegistrant {
@Desc("Set to true if you want to apply all of the settings here to the mob, even though an external plugin has already done so. Scripts are always applied.")
private boolean applySettingsToCustomMobAnyways = false;
@Desc("Set the entity type to UNKNOWN, then define a script here which ends with the entity variable (the result). You can use Iris.getLocation() to find the target location. You can spawn any entity this way.")
@Desc("Set the entity type to UNKNOWN, then define a script here which ends with the entity variable (the result). You can use location to find the target location. You can spawn any entity this way.\nFile extension: .spawn.kts")
@RegistryListResource(IrisScript.class)
private String spawnerScript = "";
@ArrayType(min = 1, type = String.class)
@Desc("Set the entity type to UNKNOWN, then define a script here. You can use Iris.getLocation() to find the target location. You can spawn any entity this way.")
@Desc("Executed post spawn you can modify the entity however you want with it\nFile extension: .postspawn.kts")
@RegistryListResource(IrisScript.class)
private KList<String> postSpawnScripts = new KList<>();
@@ -58,7 +58,7 @@ public class IrisGeneratorStyle {
private String expression = null;
@Desc("Use an Image map instead of a generated value")
private IrisImageMap imageMap = null;
@Desc("Instead of using the style property, use a custom noise generator to represent this style.")
@Desc("Instead of using the style property, use a custom noise generator to represent this style.\nFile extension: .noise.kts")
@RegistryListResource(IrisScript.class)
private String script = null;
@MinNumber(0.00001)
@@ -30,18 +30,15 @@ import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.IrisCustomData;
import com.volmit.iris.util.data.VectorMap;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.interpolation.IrisInterpolation;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.math.AxisAlignedBB;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.math.*;
import com.volmit.iris.util.matter.MatterMarker;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.IrisLock;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.scheduling.jobs.Job;
import com.volmit.iris.util.stream.ProceduralStream;
@@ -65,7 +62,10 @@ import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
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.stream.StreamSupport;
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@@ -75,17 +75,17 @@ public class IrisObject extends IrisRegistrant {
protected static final BlockData VAIR = B.get("VOID_AIR");
protected static final BlockData VAIR_DEBUG = B.get("COBWEB");
protected static final BlockData[] SNOW_LAYERS = new BlockData[]{B.get("minecraft:snow[layers=1]"), B.get("minecraft:snow[layers=2]"), B.get("minecraft:snow[layers=3]"), B.get("minecraft:snow[layers=4]"), B.get("minecraft:snow[layers=5]"), B.get("minecraft:snow[layers=6]"), B.get("minecraft:snow[layers=7]"), B.get("minecraft:snow[layers=8]")};
protected transient final IrisLock readLock = new IrisLock("read-conclock");
protected transient final Lock readLock;
protected transient final Lock writeLock;
@Getter
@Setter
protected transient volatile boolean smartBored = false;
@Getter
@Setter
protected transient IrisLock lock = new IrisLock("Preloadcache");
@Setter
protected transient AtomicCache<AxisAlignedBB> aabb = new AtomicCache<>();
private KMap<BlockVector, BlockData> blocks;
private KMap<BlockVector, TileData> states;
@Getter
private VectorMap<BlockData> blocks;
@Getter
private VectorMap<TileData> states;
@Getter
@Setter
private int w;
@@ -97,15 +97,21 @@ public class IrisObject extends IrisRegistrant {
private int h;
@Getter
@Setter
private transient BlockVector center;
private transient Vector3i center;
@Getter
private transient Vector3i shrinkOffset;
public IrisObject(int w, int h, int d) {
blocks = new KMap<>();
states = new KMap<>();
blocks = new VectorMap<>();
states = new VectorMap<>();
this.w = w;
this.h = h;
this.d = d;
center = new BlockVector(w / 2, h / 2, d / 2);
center = new Vector3i(w / 2, h / 2, d / 2);
shrinkOffset = new Vector3i(0, 0, 0);
var lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
}
public IrisObject() {
@@ -161,10 +167,10 @@ public class IrisObject extends IrisRegistrant {
PrecisionStopwatch p = PrecisionStopwatch.start();
BlockData vair = debug ? VAIR_DEBUG : VAIR;
lock.lock();
writeLock.lock();
AtomicInteger applied = new AtomicInteger();
if (getBlocks().isEmpty()) {
lock.unlock();
if (blocks.isEmpty()) {
writeLock.unlock();
Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it.");
smartBored = true;
return;
@@ -173,7 +179,7 @@ public class IrisObject extends IrisRegistrant {
BlockVector max = new BlockVector(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE);
BlockVector min = new BlockVector(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
for (BlockVector i : getBlocks().keySet()) {
for (BlockVector i : blocks.keys()) {
max.setX(Math.max(i.getX(), max.getX()));
min.setX(Math.min(i.getX(), min.getX()));
max.setY(Math.max(i.getY(), max.getY()));
@@ -193,7 +199,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE;
for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) {
if (getBlocks().containsKey(new BlockVector(ray, finalRayY, rayZ))) {
if (blocks.containsKey(new Vector3i(ray, finalRayY, rayZ))) {
start = Math.min(ray, start);
end = Math.max(ray, end);
}
@@ -201,10 +207,10 @@ public class IrisObject extends IrisRegistrant {
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
for (int i = start; i <= end; i++) {
BlockVector v = new BlockVector(i, finalRayY, rayZ);
Vector3i v = new Vector3i(i, finalRayY, rayZ);
if (!B.isAir(getBlocks().get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair);
if (!vair.equals(blocks.get(v))) {
blocks.computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement();
}
}
@@ -222,7 +228,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE;
for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) {
if (getBlocks().containsKey(new BlockVector(finalRayX, ray, rayZ))) {
if (blocks.containsKey(new Vector3i(finalRayX, ray, rayZ))) {
start = Math.min(ray, start);
end = Math.max(ray, end);
}
@@ -230,10 +236,10 @@ public class IrisObject extends IrisRegistrant {
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
for (int i = start; i <= end; i++) {
BlockVector v = new BlockVector(finalRayX, i, rayZ);
Vector3i v = new Vector3i(finalRayX, i, rayZ);
if (!B.isAir(getBlocks().get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair);
if (!vair.equals(blocks.get(v))) {
blocks.computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement();
}
}
@@ -251,7 +257,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE;
for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) {
if (getBlocks().containsKey(new BlockVector(finalRayX, rayY, ray))) {
if (blocks.containsKey(new Vector3i(finalRayX, rayY, ray))) {
start = Math.min(ray, start);
end = Math.max(ray, end);
}
@@ -259,10 +265,10 @@ public class IrisObject extends IrisRegistrant {
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
for (int i = start; i <= end; i++) {
BlockVector v = new BlockVector(finalRayX, rayY, i);
Vector3i v = new Vector3i(finalRayX, rayY, i);
if (!B.isAir(getBlocks().get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair);
if (!vair.equals(blocks.get(v))) {
blocks.computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement();
}
}
@@ -273,7 +279,7 @@ public class IrisObject extends IrisRegistrant {
burst.complete();
smartBored = true;
lock.unlock();
writeLock.unlock();
Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")");
}
@@ -284,13 +290,8 @@ public class IrisObject extends IrisRegistrant {
o.setLoadFile(getLoadFile());
o.setCenter(getCenter().clone());
for (BlockVector i : getBlocks().keySet()) {
o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone());
}
for (BlockVector i : getStates().keySet()) {
o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone());
}
blocks.forEach((i, v) -> o.blocks.put(i.clone(), v.clone()));
states.forEach((i, v) -> o.states.put(i.clone(), v.clone()));
return o;
}
@@ -300,22 +301,24 @@ public class IrisObject extends IrisRegistrant {
this.w = din.readInt();
this.h = din.readInt();
this.d = din.readInt();
center = new BlockVector(w / 2, h / 2, d / 2);
center = new Vector3i(w / 2, h / 2, d / 2);
int s = din.readInt();
for (int i = 0; i < s; i++) {
getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF()));
blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF()));
}
if (din.available() == 0)
return;
try {
int size = din.readInt();
for (int i = 0; i < size; i++) {
getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
states.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
}
} catch (Throwable e) {
Iris.reportError(e);
}
}
@@ -325,9 +328,9 @@ public class IrisObject extends IrisRegistrant {
this.h = din.readInt();
this.d = din.readInt();
if (!din.readUTF().equals("Iris V2 IOB;")) {
return;
throw new HeaderException();
}
center = new BlockVector(w / 2, h / 2, d / 2);
center = new Vector3i(w / 2, h / 2, d / 2);
int s = din.readShort();
int i;
KList<String> palette = new KList<>();
@@ -339,13 +342,13 @@ public class IrisObject extends IrisRegistrant {
s = din.readInt();
for (i = 0; i < s; i++) {
getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort())));
blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort())));
}
s = din.readInt();
for (i = 0; i < s; i++) {
getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
states.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
}
}
@@ -357,7 +360,7 @@ public class IrisObject extends IrisRegistrant {
dos.writeUTF("Iris V2 IOB;");
KList<String> palette = new KList<>();
for (BlockData i : getBlocks().values()) {
for (BlockData i : blocks.values()) {
palette.addIfMissing(i.getAsString());
}
@@ -367,21 +370,23 @@ public class IrisObject extends IrisRegistrant {
dos.writeUTF(i);
}
dos.writeInt(getBlocks().size());
dos.writeInt(blocks.size());
for (BlockVector i : getBlocks().keySet()) {
for (var entry : blocks) {
var i = entry.getKey();
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString()));
dos.writeShort(palette.indexOf(entry.getValue().getAsString()));
}
dos.writeInt(getStates().size());
for (BlockVector i : getStates().keySet()) {
dos.writeInt(states.size());
for (var entry : states) {
var i = entry.getKey();
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
getStates().get(i).toBinary(dos);
entry.getValue().toBinary(dos);
}
}
@@ -389,7 +394,7 @@ public class IrisObject extends IrisRegistrant {
AtomicReference<IOException> ref = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
new Job() {
private int total = getBlocks().size() * 3 + getStates().size();
private int total = blocks.size() * 3 + states.size();
private int c = 0;
@Override
@@ -408,11 +413,11 @@ public class IrisObject extends IrisRegistrant {
KList<String> palette = new KList<>();
for (BlockData i : getBlocks().values()) {
for (BlockData i : blocks.values()) {
palette.addIfMissing(i.getAsString());
++c;
}
total -= getBlocks().size() - palette.size();
total -= blocks.size() - palette.size();
dos.writeShort(palette.size());
@@ -421,22 +426,24 @@ public class IrisObject extends IrisRegistrant {
++c;
}
dos.writeInt(getBlocks().size());
dos.writeInt(blocks.size());
for (BlockVector i : getBlocks().keySet()) {
for (var entry : blocks) {
var i = entry.getKey();
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString()));
dos.writeShort(palette.indexOf(entry.getValue().getAsString()));
++c;
}
dos.writeInt(getStates().size());
for (BlockVector i : getStates().keySet()) {
dos.writeInt(states.size());
for (var entry : states) {
var i = entry.getKey();
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
getStates().get(i).toBinary(dos);
entry.getValue().toBinary(dos);
++c;
}
} catch (IOException e) {
@@ -468,16 +475,14 @@ public class IrisObject extends IrisRegistrant {
}
public void read(File file) throws IOException {
var fin = new BufferedInputStream(new FileInputStream(file));
try {
try (var fin = new BufferedInputStream(new FileInputStream(file))) {
read(fin);
fin.close();
} catch (Throwable e) {
Iris.reportError(e);
fin.close();
fin = new BufferedInputStream(new FileInputStream(file));
readLegacy(fin);
fin.close();
if (!(e instanceof HeaderException))
Iris.reportError(e);
try (var fin = new BufferedInputStream(new FileInputStream(file))) {
readLegacy(fin);
}
}
}
@@ -502,10 +507,11 @@ public class IrisObject extends IrisRegistrant {
}
public void shrinkwrap() {
BlockVector min = new BlockVector();
BlockVector max = new BlockVector();
if (blocks.isEmpty()) return;
BlockVector min = new BlockVector(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
BlockVector max = new BlockVector(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
for (BlockVector i : getBlocks().keySet()) {
for (BlockVector i : blocks.keys()) {
min.setX(Math.min(min.getX(), i.getX()));
min.setY(Math.min(min.getY(), i.getY()));
min.setZ(Math.min(min.getZ(), i.getZ()));
@@ -517,62 +523,77 @@ public class IrisObject extends IrisRegistrant {
w = max.getBlockX() - min.getBlockX() + 1;
h = max.getBlockY() - min.getBlockY() + 1;
d = max.getBlockZ() - min.getBlockZ() + 1;
center = new BlockVector(w / 2, h / 2, d / 2);
center = new Vector3i(w / 2, h / 2, d / 2);
Vector3i offset = new Vector3i(
-center.getBlockX() - min.getBlockX(),
-center.getBlockY() - min.getBlockY(),
-center.getBlockZ() - min.getBlockZ()
);
if (offset.getBlockX() == 0 && offset.getBlockY() == 0 && offset.getBlockZ() == 0)
return;
VectorMap<BlockData> b = new VectorMap<>();
VectorMap<TileData> s = new VectorMap<>();
blocks.forEach((vector, data) -> {
vector.add(offset);
b.put(vector, data);
});
states.forEach((vector, data) -> {
vector.add(offset);
s.put(vector, data);
});
shrinkOffset = offset;
blocks = b;
states = s;
}
public void clean() {
KMap<BlockVector, BlockData> d = new KMap<>();
VectorMap<BlockData> d = new VectorMap<>();
d.putAll(blocks);
for (BlockVector i : getBlocks().keySet()) {
d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i)));
}
KMap<BlockVector, TileData> dx = new KMap<>();
for (BlockVector i : getBlocks().keySet()) {
d.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getBlocks().get(i)));
}
for (BlockVector i : getStates().keySet()) {
dx.put(new BlockVector(i.getBlockX(), i.getBlockY(), i.getBlockZ()), Objects.requireNonNull(getStates().get(i)));
}
VectorMap<TileData> dx = new VectorMap<>();
dx.putAll(states);
blocks = d;
states = dx;
}
public BlockVector getSigned(int x, int y, int z) {
public Vector3i getSigned(int x, int y, int z) {
if (x >= w || y >= h || z >= d) {
throw new RuntimeException(x + " " + y + " " + z + " exceeds limit of " + w + " " + h + " " + d);
}
return new BlockVector(x, y, z).subtract(center).toBlockVector();
return (Vector3i) new Vector3i(x, y, z).subtract(center);
}
public void setUnsigned(int x, int y, int z, BlockData block) {
BlockVector v = getSigned(x, y, z);
Vector3i v = getSigned(x, y, z);
if (block == null) {
getBlocks().remove(v);
getStates().remove(v);
blocks.remove(v);
states.remove(v);
} else {
getBlocks().put(v, block);
blocks.put(v, block);
}
}
public void setUnsigned(int x, int y, int z, Block block, boolean legacy) {
BlockVector v = getSigned(x, y, z);
Vector3i v = getSigned(x, y, z);
if (block == null) {
getBlocks().remove(v);
getStates().remove(v);
blocks.remove(v);
states.remove(v);
} else {
BlockData data = block.getBlockData();
getBlocks().put(v, data);
blocks.put(v, data);
TileData state = TileData.getTileState(block, legacy);
if (state != null) {
Iris.debug("Saved State " + v);
getStates().put(v, state);
states.put(v, state);
}
}
}
@@ -869,6 +890,9 @@ public class IrisObject extends IrisRegistrant {
try {
if (config.getMarkers().isNotEmpty() && placer.getEngine() != null) {
markers = new KMap<>();
var list = StreamSupport.stream(blocks.keys().spliterator(), false)
.collect(KList.collector());
for (IrisObjectMarker j : config.getMarkers()) {
IrisMarker marker = getLoader().getMarkerLoader().load(j.getMarker());
@@ -877,13 +901,12 @@ public class IrisObject extends IrisRegistrant {
}
int max = j.getMaximumMarkers();
for (BlockVector i : getBlocks().k().shuffle()) {
for (BlockVector i : list.shuffle()) {
if (max <= 0) {
break;
}
BlockData data = getBlocks().get(i);
BlockData data = blocks.get(i);
for (BlockData k : j.getMark(rdata)) {
if (max <= 0) {
@@ -891,8 +914,8 @@ public class IrisObject extends IrisRegistrant {
}
if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) {
boolean a = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 1, 0))));
boolean fff = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 2, 0))));
boolean a = !blocks.containsKey((BlockVector) i.clone().add(new BlockVector(0, 1, 0)));
boolean fff = !blocks.containsKey((BlockVector) i.clone().add(new BlockVector(0, 2, 0)));
if (!marker.isEmptyAbove() || (a && fff)) {
markers.put(i, j.getMarker());
@@ -904,13 +927,14 @@ public class IrisObject extends IrisRegistrant {
}
}
for (BlockVector g : getBlocks().keySet()) {
for (var entry : blocks) {
var g = entry.getKey();
BlockData d;
TileData tile = null;
try {
d = getBlocks().get(g);
tile = getStates().get(g);
d = entry.getValue();
tile = states.get(g);
} catch (Throwable e) {
Iris.reportError(e);
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)");
@@ -1028,12 +1052,12 @@ public class IrisObject extends IrisRegistrant {
if (stilting) {
readLock.lock();
IrisStiltSettings settings = config.getStiltSettings();
for (BlockVector g : getBlocks().keySet()) {
for (BlockVector g : blocks.keys()) {
BlockData d;
if (settings == null || settings.getPalette() == null) {
try {
d = getBlocks().get(g);
d = blocks.get(g);
} catch (Throwable e) {
Iris.reportError(e);
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)");
@@ -1140,59 +1164,60 @@ public class IrisObject extends IrisRegistrant {
}
public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) {
KMap<BlockVector, BlockData> d = new KMap<>();
writeLock.lock();
VectorMap<BlockData> d = new VectorMap<>();
for (BlockVector i : getBlocks().keySet()) {
d.put(r.rotate(i.clone(), spinx, spiny, spinz), r.rotate(getBlocks().get(i).clone(),
spinx, spiny, spinz));
for (var entry : blocks) {
d.put(r.rotate(entry.getKey(), spinx, spiny, spinz), r.rotate(entry.getValue(), spinx, spiny, spinz));
}
KMap<BlockVector, TileData> dx = new KMap<>();
VectorMap<TileData> dx = new VectorMap<>();
for (BlockVector i : getStates().keySet()) {
dx.put(r.rotate(i.clone(), spinx, spiny, spinz), getStates().get(i));
for (var entry : states) {
dx.put(r.rotate(entry.getKey(), spinx, spiny, spinz), entry.getValue());
}
blocks = d;
states = dx;
shrinkwrap();
writeLock.unlock();
}
public void place(Location at) {
for (BlockVector 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();
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());
Objects.requireNonNull(getStates().get(i)).toBukkitTry(b);
Objects.requireNonNull(states.get(i)).toBukkitTry(b);
}
}
readLock.unlock();
}
public void placeCenterY(Location at) {
for (BlockVector 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();
b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false);
b.setBlockData(Objects.requireNonNull(entry.getValue()), false);
if (getStates().containsKey(i)) {
Objects.requireNonNull(getStates().get(i)).toBukkitTry(b);
if (states.containsKey(i)) {
Objects.requireNonNull(states.get(i)).toBukkitTry(b);
}
}
}
public synchronized KMap<BlockVector, BlockData> getBlocks() {
return blocks;
}
public synchronized KMap<BlockVector, TileData> getStates() {
return states;
readLock.unlock();
}
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);
}
readLock.unlock();
}
public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) {
@@ -1204,7 +1229,7 @@ public class IrisObject extends IrisRegistrant {
IrisPosition l1 = getAABB().max();
IrisPosition l2 = getAABB().min();
@SuppressWarnings({"unchecked", "rawtypes"}) HashMap<BlockVector, BlockData> placeBlock = new HashMap();
VectorMap<BlockData> placeBlock = new VectorMap<>();
Vector center = getCenter();
if (getH() == 2) {
@@ -1219,17 +1244,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)));
for (Map.Entry<BlockVector, BlockData> entry : blocks.entrySet()) {
readLock.lock();
for (var entry : blocks) {
BlockData bd = entry.getValue();
placeBlock.put(entry.getKey().clone().add(HALF).subtract(center)
.multiply(scale).add(sm1).toBlockVector(), bd);
}
readLock.unlock();
for (Map.Entry<BlockVector, BlockData> entry : placeBlock.entrySet()) {
for (var entry : placeBlock) {
BlockVector v = entry.getKey();
if (scale > 1) {
for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) {
oo.getBlocks().put(vec, entry.getValue());
oo.blocks.put(vec, entry.getValue());
}
} else {
oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue());
@@ -1248,8 +1275,9 @@ public class IrisObject extends IrisRegistrant {
}
public void trilinear(int rad) {
KMap<BlockVector, BlockData> v = getBlocks().copy();
KMap<BlockVector, BlockData> b = new KMap<>();
writeLock.lock();
VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv();
@@ -1274,11 +1302,13 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
writeLock.unlock();
}
public void tricubic(int rad) {
KMap<BlockVector, BlockData> v = getBlocks().copy();
KMap<BlockVector, BlockData> b = new KMap<>();
writeLock.lock();
VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv();
@@ -1303,6 +1333,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
writeLock.unlock();
}
public void trihermite(int rad) {
@@ -1310,8 +1341,9 @@ public class IrisObject extends IrisRegistrant {
}
public void trihermite(int rad, double tension, double bias) {
KMap<BlockVector, BlockData> v = getBlocks().copy();
KMap<BlockVector, BlockData> b = new KMap<>();
writeLock.lock();
VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv();
@@ -1336,11 +1368,13 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
writeLock.unlock();
}
private BlockData nearestBlockData(int x, int y, int 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()) {
return r;
@@ -1348,7 +1382,7 @@ public class IrisObject extends IrisRegistrant {
double d = Double.MAX_VALUE;
for (Map.Entry<BlockVector, BlockData> entry : blocks.entrySet()) {
for (var entry : blocks) {
BlockData dat = entry.getValue();
if (dat.getMaterial().isAir()) {
@@ -1362,6 +1396,7 @@ public class IrisObject extends IrisRegistrant {
r = dat;
}
}
readLock.unlock();
return r;
}
@@ -1383,4 +1418,10 @@ public class IrisObject extends IrisRegistrant {
@Override
public void scanForErrors(JSONObject p, VolmitSender sender) {
}
private static class HeaderException extends IOException {
public HeaderException() {
super("Invalid Header");
}
}
}
@@ -28,13 +28,13 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Axis;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.*;
import org.bukkit.block.data.type.RedstoneWire;
import org.bukkit.block.data.type.Wall;
import org.bukkit.block.structure.StructureRotation;
import org.bukkit.util.BlockVector;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -101,15 +101,15 @@ public class IrisObjectRotation {
return e.rotateCopy(this);
}
public IrisJigsawPiece rotateCopy(IrisJigsawPiece v) {
public IrisJigsawPiece rotateCopy(IrisJigsawPiece v, IrisPosition offset) {
IrisJigsawPiece piece = v.copy();
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
i.setPosition(rotate(i.getPosition()));
i.setPosition(rotate(i.getPosition()).add(offset));
i.setDirection(rotate(i.getDirection()));
}
try {
var translate = piece.getPlacementOptions().getTranslate();
var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ()));
var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ())).add(offset);
translate.setX(pos.getX()).setY(pos.getY()).setZ(pos.getZ());
} catch (NullPointerException ignored) {}
@@ -259,14 +259,14 @@ public class IrisObjectRotation {
g.setRotation(face);
} else if (d instanceof Orientable) {
BlockFace f = getFace(((Orientable) d).getAxis());
} else if (d instanceof Orientable g) {
BlockFace f = getFace(g.getAxis());
BlockVector bv = new BlockVector(f.getModX(), f.getModY(), f.getModZ());
bv = rotate(bv.clone(), spinx, spiny, spinz);
Axis a = getAxis(bv);
if (!a.equals(((Orientable) d).getAxis()) && ((Orientable) d).getAxes().contains(a)) {
((Orientable) d).setAxis(a);
if (!a.equals(g.getAxis()) && g.getAxes().contains(a)) {
g.setAxis(a);
}
} else if (d instanceof MultipleFacing g) {
List<BlockFace> faces = new KList<>();
@@ -304,14 +304,22 @@ public class IrisObjectRotation {
for (BlockFace i : WALL_FACES) {
wall.setHeight(i, faces.getOrDefault(i, Wall.Height.NONE));
}
} else if (d.getMaterial().equals(Material.NETHER_PORTAL) && d instanceof Orientable g) {
//TODO: Fucks up logs
BlockFace f = faceForAxis(g.getAxis());
BlockVector bv = new BlockVector(f.getModX(), f.getModY(), f.getModZ());
bv = rotate(bv.clone(), spinx, spiny, spinz);
BlockFace t = getFace(bv);
Axis a = !g.getAxes().contains(Axis.Y) ? axisFor(t) : axisFor2D(t);
((Orientable) d).setAxis(a);
} else if (d instanceof RedstoneWire wire) {
Map<BlockFace, RedstoneWire.Connection> faces = new HashMap<>();
var allowed = wire.getAllowedFaces();
for (BlockFace i : allowed) {
RedstoneWire.Connection connection = wire.getFace(i);
BlockVector bv = new BlockVector(i.getModX(), i.getModY(), i.getModZ());
bv = rotate(bv.clone(), spinx, spiny, spinz);
BlockFace r = getFace(bv);
if (allowed.contains(r))
faces.put(r, connection);
}
for (BlockFace i : allowed) {
wire.setFace(i, faces.getOrDefault(i, RedstoneWire.Connection.NONE));
}
}
} catch (Throwable e) {
Iris.reportError(e);
@@ -18,7 +18,7 @@ public class IrisPreProcessors {
private String type = "dimension";
@Required
@Desc("The preprocessor scripts")
@Desc("The preprocessor scripts\nFile extension: .proc.kts")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> scripts = new KList<>();
@@ -166,6 +166,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
return targetCache.aquire(() -> {
IrisData data = IrisData.get(dataLocation);
data.dump();
data.clearLists();
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) {
@@ -205,7 +207,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
Chunk c = PaperLib.getChunkAtAsync(world, x, z)
.thenApply(d -> {
d.addPluginChunkTicket(Iris.instance);
Iris.tickets.addTicket(d);
for (Entity ee : d.getEntities()) {
if (ee instanceof Player) {
@@ -215,7 +217,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
ee.remove();
}
engine.getWorldManager().onChunkLoad(d, false);
return d;
}).get();
@@ -241,7 +242,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRunAsync(() -> {
c.removePluginChunkTicket(Iris.instance);
Iris.tickets.removeTicket(c);
engine.getWorldManager().onChunkLoad(c, true);
}, syncExecutor)
.get();
@@ -2,24 +2,55 @@ package com.volmit.iris.util.cache;
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> {
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() {
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) {
int key = ((z & 15) * 16) + (x & 15);
T t = cache.get(key);
if (t == null) {
t = resolver.apply(x, z);
cache.set(key, t);
var entry = cache[key];
if (entry == null) {
entry = FAST ? new FastEntry<>() : new Entry<>();
if (!AA.compareAndSet(cache, key, null, entry)) {
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;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.function.Function2;
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;
public WorldCache2D(Function2<Integer, Integer, T> resolver) {
public WorldCache2D(Function2<Integer, Integer, T> resolver, int size) {
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) {
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);
}
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;
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) {
@@ -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;
}
}
@@ -63,14 +63,20 @@ public class IrisContext {
}
}
public static void dereference() {
for (Thread i : context.k()) {
if (!i.isAlive() || context.get(i).engine.isClosed()) {
if (context.get(i).engine.isClosed()) {
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.threadId());
}
public static synchronized void dereference() {
var it = context.entrySet().iterator();
while (it.hasNext()) {
var entry = it.next();
var thread = entry.getKey();
var context = entry.getValue();
if (thread == null || context == null) {
it.remove();
continue;
}
context.remove(i);
if (!thread.isAlive() || context.engine.isClosed()) {
Iris.debug("Dereferenced Context<Engine> " + thread.getName() + " " + thread.threadId());
it.remove();
}
}
}
@@ -360,6 +360,7 @@ public class B {
public static boolean isFoliagePlantable(BlockData d) {
return d.getMaterial().equals(Material.GRASS_BLOCK)
|| d.getMaterial().equals(Material.MOSS_BLOCK)
|| d.getMaterial().equals(Material.ROOTED_DIRT)
|| d.getMaterial().equals(Material.DIRT)
|| d.getMaterial().equals(Material.COARSE_DIRT)
@@ -368,6 +369,7 @@ public class B {
public static boolean isFoliagePlantable(Material d) {
return d.equals(Material.GRASS_BLOCK)
|| d.equals(Material.MOSS_BLOCK)
|| d.equals(Material.DIRT)
|| d.equals(TALL_GRASS)
|| d.equals(TALL_SEAGRASS)
@@ -1,127 +1,72 @@
package com.volmit.iris.util.data;
import com.volmit.iris.core.link.Identifier;
import lombok.Data;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.SoundGroup;
import org.bukkit.block.*;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.structure.Mirror;
import org.bukkit.block.structure.StructureRotation;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Data
public class IrisCustomData implements BlockData {
private final @NonNull BlockData base;
private final @NotNull Identifier custom;
import java.lang.reflect.Proxy;
import java.util.*;
@NotNull
@Override
public Material getMaterial() {
return base.getMaterial();
public interface IrisCustomData extends BlockData {
@NonNull BlockData getBase();
@NonNull Identifier getCustom();
static IrisCustomData of(@NotNull BlockData base, @NotNull Identifier custom) {
var clazz = base.getClass();
var loader = IrisCustomData.class.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
@Override
public String getAsString() {
return base.getAsString();
}
@ApiStatus.Internal
abstract class Internal {
private static final KMap<Class<?>, Class<?>[]> cache = new KMap<>();
@NotNull
@Override
public String getAsString(boolean b) {
return base.getAsString(b);
}
private static Class<?>[] getInterfaces(ClassLoader loader, Class<?> base) {
return cache.computeIfAbsent(base, k -> {
Set<Class<?>> set = new HashSet<>();
@NotNull
@Override
public BlockData merge(@NotNull BlockData blockData) {
return new IrisCustomData(base.merge(blockData), custom);
}
Class<?> i = base;
while (i != null) {
if (!BlockData.class.isAssignableFrom(i))
break;
@Override
public boolean matches(@Nullable BlockData blockData) {
if (blockData instanceof IrisCustomData b)
return custom.equals(b.custom) && base.matches(b.base);
return base.matches(blockData);
}
for (Class<?> j : i.getInterfaces()) {
if (j.isSealed() || j.isHidden())
continue;
@NotNull
@Override
public BlockData clone() {
return new IrisCustomData(base.clone(), custom);
}
try {
Class.forName(j.getName(), false, loader);
set.add(j);
} catch (ClassNotFoundException ignored) {}
}
@NotNull
@Override
public SoundGroup getSoundGroup() {
return base.getSoundGroup();
}
i = i.getSuperclass();
}
@Override
public int getLightEmission() {
return base.getLightEmission();
}
@Override
public boolean isOccluding() {
return base.isOccluding();
}
@Override
public boolean requiresCorrectToolForDrops() {
return base.requiresCorrectToolForDrops();
}
@Override
public boolean isPreferredTool(@NotNull ItemStack itemStack) {
return base.isPreferredTool(itemStack);
}
@NotNull
@Override
public PistonMoveReaction getPistonMoveReaction() {
return base.getPistonMoveReaction();
}
@Override
public boolean isSupported(@NotNull Block block) {
return base.isSupported(block);
}
@Override
public boolean isSupported(@NotNull Location location) {
return base.isSupported(location);
}
@Override
public boolean isFaceSturdy(@NotNull BlockFace blockFace, @NotNull BlockSupport blockSupport) {
return base.isFaceSturdy(blockFace, blockSupport);
}
@NotNull
@Override
public Material getPlacementMaterial() {
return base.getPlacementMaterial();
}
@Override
public void rotate(@NotNull StructureRotation structureRotation) {
base.rotate(structureRotation);
}
@Override
public void mirror(@NotNull Mirror mirror) {
base.mirror(mirror);
}
@NotNull
@Override
public BlockState createBlockState() {
return base.createBlockState();
set.add(IrisCustomData.class);
return set.toArray(Class<?>[]::new);
});
}
}
}
@@ -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;
}
}
}
@@ -20,6 +20,7 @@ package com.volmit.iris.util.format;
import com.volmit.iris.Iris;
import com.volmit.iris.util.plugin.VolmitSender;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import org.apache.commons.lang.Validate;
@@ -495,6 +496,14 @@ public enum C {
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
}
public static String strip(final String input) {
if (input == null) {
return null;
}
return MiniMessage.miniMessage().stripTags(stripColor(input));
}
/**
* DyeColor to ChatColor
*
@@ -19,21 +19,30 @@
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint;
import lombok.Synchronized;
import it.unimi.dsi.fastutil.ints.*;
import java.io.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
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 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();
private final Lock read, write;
private volatile Palette<T> palette;
private volatile DataBits data;
private final int length;
private final Writable<T> writer;
public DataContainer(Writable<T> writer, int length) {
var lock = new ReentrantReadWriteLock();
this.read = lock.readLock();
this.write = lock.writeLock();
this.writer = writer;
this.length = 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 {
var lock = new ReentrantReadWriteLock();
this.read = lock.readLock();
this.write = lock.writeLock();
this.writer = writer;
this.length = Varint.readUnsignedVarInt(din);
this.palette = newPalette(din);
this.data = new DataBits(palette.bits(), length, din);
trim();
}
private static int[] computeBitLimits() {
@@ -86,13 +100,18 @@ public class DataContainer<T> {
writeDos(new DataOutputStream(out));
}
@Synchronized
public void writeDos(DataOutputStream dos) throws IOException {
Varint.writeUnsignedVarInt(length, dos);
Varint.writeUnsignedVarInt(palette.size(), dos);
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
data.write(dos);
dos.flush();
write.lock();
try {
trim();
Varint.writeUnsignedVarInt(length, dos);
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 {
@@ -110,25 +129,38 @@ public class DataContainer<T> {
return new HashPalette<>();
}
@Synchronized
public void set(int position, T t) {
int id = palette.id(t);
int id;
if (id == -1) {
id = palette.add(t);
updateBits();
read.lock();
try {
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")
private void updateBits() {
if (palette.bits() == data.getBits())
int bits = palette.bits();
if (bits == data.getBits())
return;
int bits = palette.bits();
if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
palette = newPalette(bits).from(palette);
}
@@ -136,18 +168,44 @@ public class DataContainer<T> {
data = data.setBits(bits);
}
@Synchronized
public T get(int position) {
int id = data.get(position);
read.lock();
try {
int id = data.get(position);
if (id <= 0) {
return null;
if (id <= 0) {
return null;
}
return palette.get(id);
} finally {
read.unlock();
}
return palette.get(id);
}
public int size() {
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;
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 AtomicInteger size;
public HashPalette() {
this.size = new AtomicInteger(1);
this.palette = new LinkedHashMap<>();
this.palette = new KMap<>();
this.lookup = new KMap<>();
}
@@ -52,13 +53,13 @@ public class HashPalette<T> implements Palette<T> {
return 0;
}
synchronized (palette) {
return palette.computeIfAbsent(t, $ -> {
return palette.computeIfAbsent(t, $ -> {
synchronized (lock) {
int index = size.getAndIncrement();
lookup.put(index, t);
return index;
});
}
}
});
}
@Override
@@ -78,7 +79,7 @@ public class HashPalette<T> implements Palette<T> {
@Override
public void iterate(Consumer2<T, Integer> c) {
synchronized (palette) {
synchronized (lock) {
for (int i = 1; i < size.get(); i++) {
c.accept(lookup.get(i), i);
}
@@ -45,25 +45,23 @@ public class LinearPalette<T> implements Palette<T> {
@Override
public int add(T t) {
if (t == null) {
return 0;
}
int index = size.getAndIncrement();
grow(index + 1);
if (palette.length() <= index)
grow(index);
palette.set(index, t);
return index;
}
private synchronized void grow(int newLength) {
if (newLength > palette.length()) {
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength);
private synchronized void grow(int lastIndex) {
if (palette.length() > lastIndex)
return;
for (int i = 0; i < palette.length(); i++) {
a.set(i, palette.get(i));
}
palette = a;
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(lastIndex + 1);
for (int i = 0; i < palette.length(); i++) {
a.set(i, palette.get(i));
}
palette = a;
}
@Override
@@ -999,7 +999,7 @@ public class IrisInterpolation {
public static double getNoise(InterpolationMethod method, int x, int z, double h, NoiseProvider noise) {
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)) {
return getBilinearNoise(x, z, h, n);
@@ -33,6 +33,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.Consumer3;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.io.IOWorker;
@@ -50,6 +51,9 @@ import java.io.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
@@ -176,8 +180,8 @@ public class Mantle {
* @return the writer
*/
@ChunkCoordinates
public MantleWriter write(EngineMantle engineMantle, int x, int z, int radius) {
return new MantleWriter(engineMantle, this, x, z, radius);
public MantleWriter write(EngineMantle engineMantle, int x, int z, int radius, boolean multicore) {
return new MantleWriter(engineMantle, this, x, z, radius, multicore);
}
/**
@@ -201,6 +205,68 @@ public class Mantle {
return get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31);
}
public void getChunks(final int minChunkX,
final int maxChunkX,
final int minChunkZ,
final int maxChunkZ,
int parallelism,
final Consumer3<Integer, Integer, MantleChunk> consumer
) {
if (parallelism <= 0) parallelism = 1;
final var lock = new Semaphore(parallelism);
final int minRegionX = minChunkX >> 5;
final int maxRegionX = maxChunkX >> 5;
final int minRegionZ = minChunkZ >> 5;
final int maxRegionZ = maxChunkZ >> 5;
final int minRelativeX = minChunkX & 31;
final int maxRelativeX = maxChunkX & 31;
final int minRelativeZ = minChunkZ & 31;
final int maxRelativeZ = maxChunkZ & 31;
final AtomicReference<Throwable> error = new AtomicReference<>();
for (int rX = minRegionX; rX <= maxRegionX; rX++) {
final int minX = rX == minRegionX ? minRelativeX : 0;
final int maxX = rX == maxRegionX ? maxRelativeX : 31;
for (int rZ = minRegionZ; rZ <= maxRegionZ; rZ++) {
final int minZ = rZ == minRegionZ ? minRelativeZ : 0;
final int maxZ = rZ == maxRegionZ ? maxRelativeZ : 31;
final int realX = rX << 5;
final int realZ = rZ << 5;
lock.acquireUninterruptibly();
final var e = error.get();
if (e != null) {
if (e instanceof RuntimeException ex) throw ex;
else if (e instanceof Error ex) throw ex;
else throw new RuntimeException(error.get());
}
getFuture(rX, rZ)
.thenAccept(region -> {
final MantleChunk zero = region.getOrCreate(0, 0).use();
try {
for (int xx = minX; xx <= maxX; xx++) {
for (int zz = minZ; zz <= maxZ; zz++) {
consumer.accept(realX + xx, realZ + zz, region.getOrCreate(xx, zz));
}
}
} finally {
zero.release();
}
})
.exceptionally(ex -> {
error.set(ex);
return null;
})
.thenRun(lock::release);
}
}
lock.acquireUninterruptibly(parallelism);
}
/**
* Flag or unflag a chunk
*
@@ -406,18 +472,6 @@ public class Mantle {
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
* specified amount of milliseconds
@@ -566,6 +620,53 @@ public class Mantle {
return get(x, z);
}
private CompletableFuture<TectonicPlate> getFuture(int x, int z) {
final boolean trim = ioTrim.tryAcquire();
final boolean unload = ioTectonicUnload.tryAcquire();
final Function<TectonicPlate, TectonicPlate> release = p -> {
if (trim) ioTrim.release();
if (unload) ioTectonicUnload.release();
return p;
};
final Supplier<CompletableFuture<TectonicPlate>> fallback = () -> getSafe(x, z)
.exceptionally(e -> {
if (e instanceof InterruptedException) {
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)");
Iris.reportError(e);
} else {
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception");
Iris.reportError(e);
e.printStackTrace();
}
return null;
})
.thenCompose(p -> {
release.apply(p);
if (p != null) return CompletableFuture.completedFuture(p);
Iris.warn("Retrying to get " + x + " " + z + " Mantle Region");
return getFuture(x, z);
});
if (!trim || !unload) {
return getSafe(x, z)
.thenApply(release)
.exceptionallyCompose(e -> {
e.printStackTrace();
return fallback.get();
});
}
Long key = key(x, z);
TectonicPlate p = loadedRegions.get(key);
if (p != null && !p.isClosed()) {
use(key);
return CompletableFuture.completedFuture(release.apply(p));
}
return fallback.get();
}
/**
* This retreives a future of the Tectonic Plate at the given coordinates.
* All methods accessing tectonic plates should go through this method
@@ -575,8 +676,8 @@ public class Mantle {
* @return the future of a tectonic plate.
*/
@RegionCoordinates
private Future<TectonicPlate> getSafe(int x, int z) {
return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> {
protected CompletableFuture<TectonicPlate> getSafe(int x, int z) {
return ioBurst.completableFuture(() -> hyperLock.withResult(x, z, () -> {
Long k = key(x, z);
use(k);
TectonicPlate r = loadedRegions.get(k);
@@ -635,7 +736,7 @@ public class Mantle {
}
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;
}
@@ -19,16 +19,14 @@
package com.volmit.iris.util.mantle;
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.ChunkRelativeBlockCoordinates;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice;
import com.volmit.iris.util.parallel.AtomicBooleanArray;
import lombok.Getter;
import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
@@ -44,13 +42,11 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
* Represents a mantle chunk. Mantle chunks contain sections of matter (see matter api)
* Mantle Chunks are fully atomic & thread safe
*/
public class MantleChunk {
public class MantleChunk extends FlaggedChunk {
@Getter
private final int x;
@Getter
private final int z;
private final AtomicBooleanArray flags;
private final Object[] flagLocks;
private final AtomicReferenceArray<Matter> sections;
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
private final AtomicBoolean closed = new AtomicBoolean(false);
@@ -63,14 +59,8 @@ public class MantleChunk {
@ChunkCoordinates
public MantleChunk(int sectionHeight, int x, int z) {
sections = new AtomicReferenceArray<>(sectionHeight);
flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1);
flagLocks = new Object[flags.length()];
this.x = x;
this.z = z;
for (int i = 0; i < flags.length(); i++) {
flagLocks[i] = new Object();
}
}
/**
@@ -84,20 +74,7 @@ public class MantleChunk {
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
this(sectionHeight, din.readByte(), din.readByte());
int s = din.readByte();
int l = version < 0 ? MantleFlag.RESERVED_FLAGS : Varint.readUnsignedVarInt(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());
}
}
readFlags(version, din);
for (int i = 0; i < s; i++) {
Iris.addPanic("read.section", "Section[" + i + "]");
@@ -154,40 +131,14 @@ public class MantleChunk {
ref.release();
}
public void copyFlags(MantleChunk chunk) {
public void copyFrom(MantleChunk chunk) {
use();
for (int i = 0; i < flags.length(); i++) {
flags.set(i, chunk.flags.get(i));
}
release();
}
public void flag(MantleFlag flag, boolean f) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
flags.set(flag.ordinal(), f);
}
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)) {
r.run();
super.copyFrom(chunk, () -> {
for (int i = 0; i < sections.length(); i++) {
sections.set(i, chunk.get(i));
}
}
}
public void raiseFlagUnchecked(MantleFlag flag, Runnable r) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
if (flags.compareAndSet(flag.ordinal(), false, true)) r.run();
}
public boolean isFlagged(MantleFlag flag) {
return flags.get(flag.ordinal());
});
release();
}
/**
@@ -248,16 +199,12 @@ public class MantleChunk {
*/
@ChunkCoordinates
public Matter getOrCreate(int section) {
Matter matter = get(section);
final Matter matter = get(section);
if (matter != null) return matter;
if (matter == null) {
matter = new IrisMatter(16, 16, 16);
if (!sections.compareAndSet(section, null, matter)) {
matter = get(section);
}
}
return matter;
final Matter instance = new IrisMatter(16, 16, 16);
final Matter value = sections.compareAndExchange(section, null, instance);
return value == null ? instance : value;
}
/**
@@ -271,16 +218,7 @@ public class MantleChunk {
dos.writeByte(x);
dos.writeByte(z);
dos.writeByte(sections.length());
Varint.writeUnsignedVarInt(flags.length(), 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);
}
writeFlags(dos);
var bytes = new ByteArrayOutputStream(8192);
var sub = new DataOutputStream(bytes);
@@ -333,6 +271,8 @@ public class MantleChunk {
}
public void deleteSlices(Class<?> c) {
if (IrisToolbelt.isRetainingMantleDataForSlice(c.getCanonicalName()))
return;
for (int i = 0; i < sections.length(); i++) {
Matter m = sections.get(i);
if (m != null && m.hasSlice(c)) {
@@ -348,4 +288,9 @@ public class MantleChunk {
}
}
}
@Override
public boolean isClosed() {
return closed.get();
}
}
@@ -175,10 +175,13 @@ public class TectonicPlate {
*/
@ChunkCoordinates
public MantleChunk getOrCreate(int x, int z) {
return chunks.updateAndGet(index(x, z), chunk -> {
if (chunk != null) return chunk;
return new MantleChunk(sectionHeight, x & 31, z & 31);
});
final int index = index(x, z);
final MantleChunk chunk = chunks.get(index);
if (chunk != null) return chunk;
final MantleChunk instance = new MantleChunk(sectionHeight, x & 31, z & 31);
final MantleChunk value = chunks.compareAndExchange(index, null, instance);
return value == null ? instance : value;
}
@ChunkCoordinates
@@ -40,6 +40,7 @@ public sealed interface MantleFlag permits CustomFlag, ReservedFlag {
MantleFlag CUSTOM = ReservedFlag.CUSTOM;
MantleFlag DISCOVERED = ReservedFlag.DISCOVERED;
MantleFlag CUSTOM_ACTIVE = ReservedFlag.CUSTOM_ACTIVE;
MantleFlag SCRIPT = ReservedFlag.SCRIPT;
int RESERVED_FLAGS = ReservedFlag.values().length;
@@ -16,7 +16,8 @@ public enum ReservedFlag implements MantleFlag {
TILE,
CUSTOM,
DISCOVERED,
CUSTOM_ACTIVE;
CUSTOM_ACTIVE,
SCRIPT;
@Override
public boolean isCustom() {
@@ -0,0 +1,26 @@
package com.volmit.iris.util.math;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public class Vector3i extends BlockVector {
public Vector3i(int x, int y, int z) {
super(x, y, z);
}
public Vector3i(Vector vec) {
super(vec);
}
@NotNull
@Override
public Vector3i clone() {
return (Vector3i) super.clone();
}
@Override
public int hashCode() {
return (int) x ^ ((int) z << 12) ^ ((int) y << 24);
}
}
@@ -85,13 +85,13 @@ public interface Matter {
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);
for (BlockVector i : object.getBlocks().keySet()) {
for (BlockVector i : object.getBlocks().keys()) {
min.setX(Math.min(min.getX(), i.getX()));
min.setY(Math.min(min.getY(), i.getY()));
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));
}
@@ -56,7 +56,7 @@ public class Bindings {
options.setEnvironment(BuildConstants.ENVIRONMENT);
options.setBeforeSend((event, hint) -> {
if (suppress(event.getThrowable())) return null;
event.setTag("iris.safeguard", IrisSafeguard.mode());
event.setTag("iris.safeguard", IrisSafeguard.mode().getId());
event.setTag("iris.nms", INMS.get().getClass().getCanonicalName());
var context = IrisContext.get();
if (context != null) event.getContexts().set("engine", context.asContext());
@@ -67,6 +67,7 @@ public class Bindings {
Sentry.configureScope(scope -> {
if (settings.includeServerId) scope.setUser(ServerID.asUser());
scope.addAttachment(Attachments.PLUGINS);
scope.addAttachment(Attachments.SAFEGUARD);
scope.setTag("server", Bukkit.getVersion());
scope.setTag("server.type", Bukkit.getName());
scope.setTag("server.api", Bukkit.getBukkitVersion());
@@ -24,6 +24,10 @@ public class AtomicBooleanArray implements Serializable {
AA.setVolatile(array, index, newValue);
}
public final boolean getAndSet(int index, boolean newValue) {
return (boolean) AA.getAndSet(array, index, newValue);
}
public final boolean compareAndSet(int index, boolean expectedValue, boolean newValue) {
return (boolean) AA.compareAndSet(array, index, expectedValue, newValue);
}
@@ -23,6 +23,8 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.ExecutorsKt;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@@ -39,7 +41,9 @@ public class MultiBurst implements ExecutorService {
private final String name;
private final int priority;
private final IntSupplier parallelism;
private ExecutorService service;
private final Object lock = new Object();
private volatile ExecutorService service;
private volatile CoroutineDispatcher dispatcher;
public MultiBurst() {
this("Iris");
@@ -60,25 +64,36 @@ public class MultiBurst implements ExecutorService {
last = new AtomicLong(M.ms());
}
private synchronized ExecutorService getService() {
private ExecutorService getService() {
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()),
new ForkJoinPool.ForkJoinWorkerThreadFactory() {
int m = 0;
new ForkJoinPool.ForkJoinWorkerThreadFactory() {
int m = 0;
@Override
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
worker.setPriority(priority);
worker.setName(name + " " + ++m);
return worker;
}
},
(t, e) -> e.printStackTrace(), true);
@Override
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
worker.setPriority(priority);
worker.setName(name + " " + ++m);
return worker;
}
},
(t, e) -> e.printStackTrace(), true);
dispatcher = ExecutorsKt.from(service);
return service;
}
}
return service;
public CoroutineDispatcher getDispatcher() {
getService();
return dispatcher;
}
public void burst(Runnable... r) {
@@ -157,6 +172,18 @@ public class MultiBurst implements ExecutorService {
return getService().submit(o);
}
public <T> CompletableFuture<T> completableFuture(Callable<T> o) {
CompletableFuture<T> f = new CompletableFuture<>();
getService().submit(() -> {
try {
f.complete(o.call());
} catch (Exception e) {
f.completeExceptionally(e);
}
});
return f;
}
@Override
public void shutdown() {
close();
@@ -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());
}
}

Some files were not shown because too many files have changed in this diff Show More