Compare commits

..

34 Commits

Author SHA1 Message Date
Julian Krings 6d3edff459 make studio tools work on folia 2025-09-21 20:54:13 +02:00
Julian Krings e06724fcf6 Merge branch 'refs/heads/dev' into feat/folia
# Conflicts:
#	core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java
#	core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java
#	core/src/main/java/com/volmit/iris/engine/framework/Engine.java
2025-09-21 20:41:11 +02:00
Julian Krings b8219fac1b Merge branch 'dev' into feat/folia
# Conflicts:
#	core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java
#	core/src/main/java/com/volmit/iris/engine/framework/Engine.java
#	core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java
2025-09-06 18:37:29 +02:00
Julian Krings bddc061f46 make chunk retrieval to teleport async 2025-08-02 23:31:17 +02:00
Julian Krings bf6af9a58d Merge branch 'dev' into feat/folia 2025-08-02 23:26:34 +02:00
Julian Krings aaf2f2f8a6 fix teleport after world creation 2025-08-02 23:15:57 +02:00
Julian Krings dc8cf0ad38 remove last usages of the bukkit scheduler 2025-08-02 23:15:21 +02:00
Julian Krings bd07f5d325 add link for Worlds to bring back world creation on folia 2025-08-02 22:23:05 +02:00
Julian Krings bd722fdacb Merge branch 'dev' into feat/folia
# Conflicts:
#	core/build.gradle.kts
#	core/src/main/java/com/volmit/iris/Iris.java
#	core/src/main/java/com/volmit/iris/core/commands/CommandIris.java
#	gradle/libs.versions.toml
2025-08-02 21:25:40 +02:00
Julian Krings d5ec6a18a4 fix loot not being applied 2025-07-26 12:31:24 +02:00
Julian Krings 2f16c0cfb7 Merge branch 'dev' into feat/folia 2025-07-25 22:34:17 +02:00
Julian Krings f7ac827692 don't relocate platform utils for now 2025-07-25 22:34:05 +02:00
Julian Krings bddc62f385 fix object saving 2025-07-20 00:50:11 +02:00
Julian Krings 68a214edb5 move platform utils to the version catalog 2025-07-19 23:02:49 +02:00
Julian Krings 49d2392c80 use platform utils for regen 2025-07-19 22:55:25 +02:00
Julian Krings fcbbd2135b apply folia modification to 1.21.7 nms 2025-07-19 22:53:06 +02:00
Julian Krings c3442ab2ce Merge branch 'dev' into feat/folia
# Conflicts:
#	build.gradle.kts
#	core/build.gradle.kts
#	core/src/main/java/com/volmit/iris/Iris.java
#	core/src/main/java/com/volmit/iris/core/commands/CommandIris.java
#	core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java
#	core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java
#	core/src/main/resources/plugin.yml
#	nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java
#	nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java
#	nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java
#	nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java
#	nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java
#	nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java
#	nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java
#	settings.gradle.kts
2025-07-19 22:50:48 +02:00
Julian Krings fd3971018b update platform utils 2025-06-23 16:16:25 +02:00
Julian Krings b440d0257d fix iris what commands 2025-06-19 19:50:08 +02:00
Julian Krings 42a26a1de2 fix spawning empty particles and improve message for invalid tile states 2025-06-19 19:43:53 +02:00
Julian Krings c8eab22427 fix a few sync teleports 2025-06-19 18:28:46 +02:00
Julian Krings fa3e35f702 fix pregen save chunk failing 2025-06-19 18:28:22 +02:00
Julian Krings cf0bc81778 replace scheduler and paperlib with platform utils 2025-06-19 17:46:08 +02:00
Julian Krings bef99f18c3 fix effects 2025-06-18 12:00:55 +02:00
Julian Krings 96a384c09c fix modern tile states not applying 2025-06-18 11:51:09 +02:00
Julian Krings d61b2205c0 handle failing restart command better 2025-06-18 11:25:41 +02:00
Julian Krings ebdfb94392 Merge branch 'dev' into feat/folia 2025-06-17 21:28:29 +02:00
Julian Krings 1c5fe016cb handle failing world creation 2025-06-15 12:36:53 +02:00
Julian Krings 0957b9baf2 Merge branch 'dev' into feat/folia
# Conflicts:
#	core/src/main/java/com/volmit/iris/Iris.java
2025-06-15 12:24:18 +02:00
Julian Krings 7570064b1a fix locate command not teleporting 2025-06-14 19:36:32 +02:00
Julian Krings e461c1e199 fix updater and mob spawning instantly failing 2025-06-14 19:35:28 +02:00
Julian Krings 35b879f0df get iris to load on folia, but it will not load worlds as this is currently not possible 2025-06-13 21:44:02 +02:00
Julian Krings ba6fac5422 more helper methods 2025-06-13 21:05:11 +02:00
Julian Krings 2577344ac0 implement platform specific schedulers 2025-06-13 19:48:42 +02:00
151 changed files with 2640 additions and 5297 deletions
+39 -19
View File
@@ -1,5 +1,8 @@
import com.volmit.nmstools.NMSToolsExtension
import com.volmit.nmstools.NMSToolsPlugin
import de.undercouch.gradle.tasks.download.Download
import xyz.jpenilla.runpaper.task.RunServer
import xyz.jpenilla.runtask.service.DownloadsAPIService
import kotlin.system.exitProcess
/*
@@ -22,18 +25,19 @@ import kotlin.system.exitProcess
buildscript {
repositories.maven("https://jitpack.io")
dependencies.classpath("com.github.VolmitSoftware:NMSTools:c88961416f")
dependencies.classpath("com.github.VolmitSoftware:NMSTools:c5cbc46ce6")
}
plugins {
java
`java-library`
alias(libs.plugins.shadow)
alias(libs.plugins.download)
alias(libs.plugins.runPaper)
}
group = "com.volmit"
version = "3.8.0-1.20.1-1.21.10"
version = "3.7.2-1.20.1-1.21.8"
apply<ApiGenerator>()
@@ -55,16 +59,14 @@ registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins")
registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins")
// ==============================================================
val serverMinHeap = "10G"
val serverMaxHeap = "10G"
val additionalFlags = "-XX:+AlwaysPreTouch"
val serverMinHeap = "2G"
val serverMaxHeap = "8G"
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
val color = "truecolor"
val errorReporting = findProperty("errorReporting") as Boolean? ?: false
val nmsBindings = mapOf(
"v1_21_R6" to "1.21.10-R0.1-SNAPSHOT",
"v1_21_R5" to "1.21.8-R0.1-SNAPSHOT",
"v1_21_R5" to "1.21.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",
@@ -75,20 +77,27 @@ val nmsBindings = mapOf(
"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>()
nmsBinding {
repositories {
maven("https://libraries.minecraft.net")
}
extensions.configure(NMSToolsExtension::class) {
jvm = jvmVersion.getOrDefault(key, 21)
version = value
type = NMSBinding.Type.DIRECT
}
dependencies {
compileOnly(project(":core"))
compileOnly(rootProject.libs.annotations)
compileOnly(rootProject.libs.byteBuddy.core)
compileOnly(rootProject.libs.platformUtils) {
isTransitive = false
}
}
}
@@ -105,23 +114,33 @@ 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 jarJar: Configuration by configurations.creating
dependencies {
for (key in nmsBindings.keys) {
implementation(project(":nms:$key", "reobf"))
tasks.register<RunServer>("runFolia-$key") {
group = "servers"
downloadsApiService = DownloadsAPIService.folia(project)
minecraftVersion(value.split("-")[0])
minHeapSize = serverMinHeap
maxHeapSize = serverMaxHeap
pluginJars(tasks.jar.flatMap { it.archiveFile })
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))}
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
systemProperty("disable.watchdog", "")
systemProperty("net.kyori.ansi.colorLevel", color)
systemProperty("com.mojang.eula.agree", true)
systemProperty("iris.suppressReporting", !errorReporting)
jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}")
}
implementation(project(":core", "shadow"))
jarJar(project(":core:agent"))
}
tasks {
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(jarJar, configurations.runtimeClasspath.map { it.resolve().map(::zipTree) })
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 })
archiveFileName.set("Iris-${project.version}.jar")
}
@@ -196,6 +215,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.thenextlvl.net/releases") //Worlds
}
dependencies {
+1 -6
View File
@@ -1,16 +1,11 @@
plugins {
kotlin("jvm") version embeddedKotlinVersion
kotlin("jvm") version "2.0.20"
}
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
@@ -1,182 +0,0 @@
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)
+12 -4
View File
@@ -67,16 +67,21 @@ dependencies {
isTransitive = false
}
compileOnly(libs.multiverseCore)
compileOnly(libs.worlds)
// Shaded
implementation(slimjarHelper("spigot"))
implementation(rootProject.libs.platformUtils) {
isTransitive = false
}
// Dynamically Loaded
slim(libs.paralithic)
slim(libs.paperlib)
slim(libs.adventure.api)
slim(libs.adventure.minimessage)
slim(libs.adventure.platform)
slim(libs.adventure.gson)
slim(libs.adventure.legacy)
slim(libs.bstats)
slim(libs.sentry)
@@ -108,6 +113,11 @@ dependencies {
slim(libs.mavenCore)
}
}
constraints {
slim(libs.gson)
compileOnly(libs.gson)
}
}
java {
@@ -131,8 +141,7 @@ slimJar {
))
relocate("com.dfsek.paralithic", "$lib.paralithic")
relocate("io.papermc.lib", "$lib.paper")
relocate("net.kyori", "$lib.kyori")
relocate("net.kyori.audience", "$lib.audience")
relocate("org.bstats", "$lib.metrics")
relocate("io.sentry", "$lib.sentry")
relocate("org.apache.maven", "$lib.maven")
@@ -142,7 +151,6 @@ 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 {
+16 -11
View File
@@ -58,11 +58,12 @@ 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;
import lombok.NonNull;
import de.crazydev22.platformutils.Platform;
import de.crazydev22.platformutils.PlatformUtils;
import org.bukkit.*;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.Command;
@@ -80,6 +81,7 @@ import java.io.*;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -96,7 +98,7 @@ public class Iris extends VolmitPlugin implements Listener {
public static MultiverseCoreLink linkMultiverseCore;
public static IrisCompat compat;
public static FileWatcher configWatcher;
public static ChunkTickets tickets;
public static Platform platform;
private static VolmitSender sender;
private static Thread shutdownHook;
@@ -315,15 +317,14 @@ public class Iris extends VolmitPlugin implements Listener {
@SuppressWarnings("deprecation")
public static void later(NastyRunnable object) {
try {
Bukkit.getScheduler().scheduleAsyncDelayedTask(instance, () ->
{
platform.getAsyncScheduler().runDelayed(task -> {
try {
object.run();
} catch (Throwable e) {
e.printStackTrace();
Iris.reportError(e);
}
}, RNG.r.i(100, 1200));
}, RNG.r.i(5, 60), TimeUnit.SECONDS);
} catch (IllegalPluginAccessException ignored) {
}
@@ -443,6 +444,7 @@ public class Iris extends VolmitPlugin implements Listener {
private void enable() {
services = new KMap<>();
platform = PlatformUtils.createPlatform(this);
setupAudience();
Bindings.setupSentry();
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
@@ -452,14 +454,13 @@ public class Iris extends VolmitPlugin implements Listener {
IrisSafeguard.IrisSafeguardSystem();
getSender().setTag(getTag());
IrisSafeguard.splash(true);
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(IrisSafeguard::suggestPaper); //TODO reimplement this
J.a(() -> IO.delete(getTemp()));
J.a(LazyPregenerator::loadLazyGenerators, 100);
J.a(this::bstats);
@@ -509,8 +510,13 @@ public class Iris extends VolmitPlugin implements Listener {
WorldCreator c = new WorldCreator(s)
.generator(gen)
.environment(dim.getEnvironment());
INMS.get().createWorld(c);
Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!");
INMS.get().createWorldAsync(c)
.thenAccept(w -> Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"))
.exceptionally(e -> {
Iris.error("Failed to load world " + s + "!");
e.printStackTrace();
return null;
});
} catch (Throwable e) {
Iris.error("Failed to load world " + s + "!");
e.printStackTrace();
@@ -531,7 +537,7 @@ public class Iris extends VolmitPlugin implements Listener {
J.s(() -> {
for (Player i : getServer().getOnlinePlayers()) {
i.setGameMode(GameMode.SPECTATOR);
i.teleport(new Location(w, 0, 200, 0));
platform.teleportAsync(i, new Location(w, 0, 200, 0));
}
});
});
@@ -565,7 +571,6 @@ public class Iris extends VolmitPlugin implements Listener {
public void onDisable() {
services.values().forEach(IrisService::onDisable);
Bukkit.getScheduler().cancelTasks(this);
HandlerList.unregisterAll((Plugin) this);
postShutdown.forEach(Runnable::run);
super.onDisable();
@@ -159,7 +159,7 @@ public class IrisSettings {
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
public boolean trimMantleInStudio = false;
public int mantleKeepAlive = 30;
public int noiseCacheSize = 1_024;
public int cacheSize = 4_096;
public int resourceLoaderCacheSize = 1_024;
public int objectLoaderCacheSize = 4_096;
public int scriptLoaderCacheSize = 512;
@@ -177,9 +177,6 @@ public class IrisSettings {
@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);
@@ -188,10 +185,6 @@ 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);
}
@@ -250,7 +243,6 @@ 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;
}
@@ -179,11 +179,14 @@ public class ServerConfigurator {
Iris.warn("New data pack entries have been installed in Iris! Restarting server!");
Iris.warn("This will only happen when your pack changes (updates/first time setup)");
Iris.warn("(You can disable this auto restart in iris settings)");
if (!Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart")) {
Iris.warn("Looks like the restart command didn't work. Stopping the server instead!");
Bukkit.shutdown();
}
J.s(() -> {
Iris.warn("Looks like the restart command didn't work. Stopping the server instead!");
Bukkit.shutdown();
}, 100);
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart");
});
}
@@ -42,7 +42,6 @@ import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.*;
import java.util.List;
@@ -158,13 +157,8 @@ public class CommandIris implements DecreeExecutor {
return;
}
new BukkitRunnable() {
@Override
public void run() {
target.teleport(world.getSpawnLocation());
new VolmitSender(target).sendMessage(C.GREEN + "You have been teleported to " + world.getName() + ".");
}
}.runTask(Iris.instance);
Iris.platform.teleportAsync(target, world.getSpawnLocation()).thenRun(() ->
new VolmitSender(target).sendMessage(C.GREEN + "You have been teleported to " + world.getName() + "."));
}
@Decree(description = "Print version information")
@@ -38,6 +38,7 @@ import com.volmit.iris.util.decree.specialhandlers.ObjectHandler;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.Direction;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.Queue;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@@ -78,9 +79,9 @@ public class CommandObject implements DecreeExecutor {
futureBlockChanges.put(block, block.getBlockData());
if (d instanceof IrisCustomData data) {
block.setBlockData(data.getBase(), false);
block.setBlockData(data.getBase());
Iris.warn("Tried to place custom block at " + x + ", " + y + ", " + z + " which is not supported!");
} else block.setBlockData(d, false);
} else block.setBlockData(d);
}
@Override
@@ -123,21 +124,11 @@ 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;
}
};
}.sync(world);
}
@Decree(description = "Check the composition of an object")
@@ -149,7 +140,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()));
var queue = o.getBlocks().values();
Queue<BlockData> queue = o.getBlocks().enqueueValues();
Map<Material, Set<BlockData>> unsorted = new HashMap<>();
Map<BlockData, Integer> amounts = new HashMap<>();
Map<Material, Integer> materials = new HashMap<>();
@@ -47,20 +47,16 @@ import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.math.*;
import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.parallel.SyncExecutor;
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.ParallelRadiusJob;
import io.papermc.lib.PaperLib;
import com.volmit.iris.util.scheduling.jobs.ParallelQueueJob;
import de.crazydev22.platformutils.scheduler.task.Task;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.util.BlockVector;
@@ -77,8 +73,8 @@ import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@@ -175,49 +171,56 @@ public class CommandStudio implements DecreeExecutor {
PlatformChunkGenerator plat = IrisToolbelt.access(world);
Engine engine = plat.getEngine();
DecreeContext.touch(sender);
try (SyncExecutor executor = new SyncExecutor(20);
var service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
) {
try (var executor = Iris.platform.createRegionExecutor(20)) {
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>();
ParallelRadiusJob prep = new ParallelRadiusJob(Integer.MAX_VALUE, service) {
ParallelQueueJob<Position2> prep = new ParallelQueueJob<>() {
@Override
protected void execute(int rX, int rZ) {
if (Math.abs(rX) <= radius && Math.abs(rZ) <= radius) {
mantle.deleteChunk(rX + x, rZ + z);
public void execute(Position2 pos) {
var cpos = pos.add(x, z);
if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) {
mantle.deleteChunk(cpos.getX(), cpos.getZ());
return;
}
rX += x;
rZ += z;
chunkMap.put(new Position2(rX, rZ), mantle.getChunk(rX, rZ));
mantle.deleteChunk(rX, rZ);
chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ()));
mantle.deleteChunk(cpos.getX(), cpos.getZ());
}
@Override
public String getName() {
return "Preparing Mantle";
}
}.retarget(radius + rad, 0, 0);
};
for (int xx = -(radius + rad); xx <= radius + rad; xx++) {
for (int zz = -(radius + rad); zz <= radius + rad; zz++) {
prep.queue(new Position2(xx, zz));
}
}
CountDownLatch pLatch = new CountDownLatch(1);
prep.execute(sender(), pLatch::countDown);
pLatch.await();
ParallelRadiusJob job = new ParallelRadiusJob(Integer.MAX_VALUE, service) {
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() {
@Override
protected void execute(int x, int z) {
plat.injectChunkReplacement(world, x, z, executor);
public void execute(Position2 p) {
plat.injectChunkReplacement(world, p.getX(), p.getZ(), executor);
}
@Override
public String getName() {
return "Regenerating";
}
}.retarget(radius, x, z);
};
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
job.queue(new Position2(i + x, j + z));
}
}
CountDownLatch latch = new CountDownLatch(1);
job.execute(sender(), latch::countDown);
latch.await();
@@ -329,31 +332,25 @@ public class CommandStudio implements DecreeExecutor {
return;
}
Player player = player();
var scheduler = Iris.platform.getEntityScheduler(player);
scheduler.run(() -> {
sender().sendMessage(C.GREEN + "Opening inventory now!");
player.openInventory(inv);
O<Integer> ta = new O<>();
ta.set(-1);
scheduler.runAtFixedRate(refresh -> {
if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
refresh.cancel();
return;
}
var sender = sender();
var player = player();
var engine = engine();
if (!add) {
inv.clear();
}
ta.set(Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () ->
{
if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
Bukkit.getScheduler().cancelTask(ta.get());
sender.sendMessage(C.GREEN + "Opened inventory!");
return;
}
if (!add) {
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);
}, 0, fast ? 5 : 35));
sender().sendMessage(C.GREEN + "Opening inventory now!");
player().openInventory(inv);
engine().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ(), 1);
}, null, 1, fast ? 5 : 35);
}, null);
}
@Decree(description = "Calculate the chance for each region to generate", origin = DecreeOrigin.PLAYER)
@@ -376,7 +373,7 @@ public class CommandStudio implements DecreeExecutor {
var loc = player.getLocation();
int totalTasks = d * d;
AtomicInteger completedTasks = new AtomicInteger(0);
int c = J.ar(() -> sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding regions"), 0);
Task c = J.ar(() -> sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding regions"), 0);
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
var region = engine.getRegion((x << 4) + 8, (z << 4) + 8);
data.computeIfAbsent(region.getLoadKey(), (k) -> new AtomicInteger(0))
@@ -385,7 +382,7 @@ public class CommandStudio implements DecreeExecutor {
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
executor.complete();
multiBurst.close();
J.car(c);
c.cancel();
sender.sendMessage(C.GREEN + "Done!");
var loader = engine.getData().getRegionLoader();
@@ -410,9 +407,7 @@ public class CommandStudio implements DecreeExecutor {
var loc = player().getLocation();
int totalTasks = d * d;
AtomicInteger completedTasks = new AtomicInteger(0);
int c = J.ar(() -> {
sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding structures");
}, 0);
var c = J.ar(() -> sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding structures"), 0);
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
var struct = engine.getStructureAt(x, z);
@@ -424,7 +419,7 @@ public class CommandStudio implements DecreeExecutor {
executor.complete();
multiBurst.close();
J.car(c);
if (c != null) c.cancel();
for (var key : data.keySet()) {
var list = data.get(key);
@@ -702,8 +697,9 @@ 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);
Player player = player();
Iris.platform.teleportAsync(player, Iris.service(StudioSVC.class).getActiveProject().getActiveProvider().getTarget().getWorld().spawnLocation());
Iris.platform.getEntityScheduler(player).run(() -> player.setGameMode(GameMode.SPECTATOR), null);
}
@Decree(description = "Update your dimension projects VSCode workspace")
@@ -719,7 +715,7 @@ public class CommandStudio implements DecreeExecutor {
}
}
@Decree(aliases = "find-objects", description = "Get information about nearby structures")
@Decree(aliases = "find-objects", description = "Get information about nearby structures", origin = DecreeOrigin.PLAYER)
public void objects() {
if (!IrisToolbelt.isIrisWorld(player().getWorld())) {
sender().sendMessage(C.RED + "You must be in an Iris world");
@@ -732,21 +728,29 @@ public class CommandStudio implements DecreeExecutor {
sender().sendMessage("You must be in an iris world.");
return;
}
KList<Chunk> chunks = new KList<>();
int bx = player().getLocation().getChunk().getX();
int bz = player().getLocation().getChunk().getZ();
KMap<Position2, CompletableFuture<Chunk>> chunks = new KMap<>();
var location = player().getLocation();
int bx = location.getBlockX() >> 4;
int bz = location.getBlockZ() >> 4;
Spiraled spiraled = (x, z) -> chunks.putIfAbsent(new Position2(x, z), Iris.platform.getChunkAtAsync(world, x, z));
try {
Location l = player().getTargetBlockExact(48, FluidCollisionMode.NEVER).getLocation();
var player = player();
var task = Iris.platform.getEntityScheduler(player).run(() -> {
var target = player.getTargetBlockExact(48, FluidCollisionMode.NEVER);
if (target == null) return;
Location l = target.getLocation();
int cx = l.getChunk().getX();
int cz = l.getChunk().getZ();
new Spiraler(3, 3, (x, z) -> chunks.addIfMissing(world.getChunkAt(x + cx, z + cz))).drain();
int cx = l.getBlockX() >> 4;
int cz = l.getBlockZ() >> 4;
new Spiraler(3, 3, (x, z) -> spiraled.on(x + cx, z + cz)).drain();
}, null);
if (task != null) task.getResult().join();
} catch (Throwable e) {
Iris.reportError(e);
}
new Spiraler(3, 3, (x, z) -> chunks.addIfMissing(world.getChunkAt(x + bx, z + bz))).drain();
new Spiraler(3, 3, (x, z) -> spiraled.on(x + bx, z + bz)).drain();
sender().sendMessage("Capturing IGenData from " + chunks.size() + " nearby chunks.");
try {
File ff = Iris.instance.getDataFile("reports/" + M.ms() + ".txt");
@@ -756,11 +760,11 @@ public class CommandStudio implements DecreeExecutor {
pw.println("Iris Version: " + Iris.instance.getDescription().getVersion());
pw.println("Bukkit Version: " + Bukkit.getBukkitVersion());
pw.println("MC Version: " + Bukkit.getVersion());
pw.println("PaperSpigot: " + (PaperLib.isPaper() ? "Yup!" : "Nope!"));
//pw.println("PaperSpigot: " + (PaperLib.isPaper() ? "Yup!" : "Nope!")); //TODO update this
pw.println("Report Captured At: " + new Date());
pw.println("Chunks: (" + chunks.size() + "): ");
for (Chunk i : chunks) {
for (Position2 i : chunks.keySet()) {
pw.println("- [" + i.getX() + ", " + i.getZ() + "]");
}
@@ -785,25 +789,31 @@ public class CommandStudio implements DecreeExecutor {
Iris.reportError(e);
}
KList<String> biomes = new KList<>();
KList<String> caveBiomes = new KList<>();
KMap<String, KMap<String, KList<String>>> objects = new KMap<>();
KSet<String> biomes = new KSet<>();
KSet<String> caveBiomes = new KSet<>();
KMap<String, KMap<String, KSet<String>>> objects = new KMap<>();
for (Chunk i : chunks) {
for (int j = 0; j < 16; j += 3) {
var engine = engine();
assert engine != null;
for (int k = 0; k < 16; k += 3) {
assert engine() != null;
IrisBiome bb = engine().getSurfaceBiome((i.getX() * 16) + j, (i.getZ() * 16) + k);
IrisBiome bxf = engine().getCaveBiome((i.getX() * 16) + j, (i.getZ() * 16) + k);
biomes.addIfMissing(bb.getName() + " [" + Form.capitalize(bb.getInferredType().name().toLowerCase()) + "] " + " (" + bb.getLoadFile().getName() + ")");
caveBiomes.addIfMissing(bxf.getName() + " (" + bxf.getLoadFile().getName() + ")");
exportObjects(bb, pw, engine(), objects);
exportObjects(bxf, pw, engine(), objects);
KList<CompletableFuture<?>> futures = new KList<>(chunks.size());
for (var future : chunks.values()) {
futures.add(future.thenAccept(i -> {
int bX = i.getX() << 4;
int bZ = i.getZ() << 4;
for (int j = 0; j < 16; j += 3) {
for (int k = 0; k < 16; k += 3) {
IrisBiome bb = engine.getSurfaceBiome(bX + j, bZ + k);
IrisBiome bxf = engine.getCaveBiome(bX + j, bZ + k);
biomes.add(bb.getName() + " [" + Form.capitalize(String.valueOf(bb.getInferredType()).toLowerCase()) + "] " + " (" + bb.getLoadFile().getName() + ")");
caveBiomes.add(bxf.getName() + " (" + bxf.getLoadFile().getName() + ")");
exportObjects(bb, pw, engine, objects);
exportObjects(bxf, pw, engine, objects);
}
}
}
}));
}
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
regions = Objects.requireNonNull(new File(world.getWorldFolder().getPath() + "/region").list()).length;
@@ -823,6 +833,13 @@ public class CommandStudio implements DecreeExecutor {
pw.println("- " + i);
}
pw.println();
pw.println("== Cave Biome Info ==");
pw.println("Found " + caveBiomes.size() + " Cave Biome(s): ");
for (String i : caveBiomes) {
pw.println("- " + i);
}
pw.println();
pw.println("== Object Info ==");
@@ -848,8 +865,8 @@ public class CommandStudio implements DecreeExecutor {
}
}
private void exportObjects(IrisBiome bb, PrintWriter pw, Engine g, KMap<String, KMap<String, KList<String>>> objects) {
String n1 = bb.getName() + " [" + Form.capitalize(bb.getInferredType().name().toLowerCase()) + "] " + " (" + bb.getLoadFile().getName() + ")";
private void exportObjects(IrisBiome bb, PrintWriter pw, Engine g, KMap<String, KMap<String, KSet<String>>> objects) {
String n1 = bb.getName() + " [" + Form.capitalize(String.valueOf(bb.getInferredType()).toLowerCase()) + "] " + " (" + bb.getLoadFile().getName() + ")";
int m = 0;
KSet<String> stop = new KSet<>();
for (IrisObjectPlacement f : bb.getObjects()) {
@@ -874,7 +891,7 @@ public class CommandStudio implements DecreeExecutor {
String n3 = nn3;
objects.computeIfAbsent(n1, (k1) -> new KMap<>())
.computeIfAbsent(n2, (k) -> new KList<>()).addIfMissing(n3);
.computeIfAbsent(n2, (k) -> new KSet<>()).add(n3);
}
}
}
@@ -74,22 +74,22 @@ public class CommandWhat implements DecreeExecutor {
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage("Non-Iris Biome: " + player().getLocation().getBlock().getBiome().name());
if (player().getLocation().getBlock().getBiome().equals(Biome.CUSTOM)) {
try {
sender().sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(player().getLocation()) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())) + ")");
} catch (Throwable ee) {
Iris.reportError(ee);
}
}
var loc = player().getLocation();
var sender = sender();
Iris.platform.getRegionScheduler().run(loc, () -> {
var biome = loc.getBlock().getBiome();
if (biome != Biome.CUSTOM && biome.getKey().getNamespace().equals("minecraft"))
return;
sender.sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(loc) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(loc)) + ")");
});
}
}
@Decree(description = "What region am i in?", origin = DecreeOrigin.PLAYER)
public void region() {
try {
Chunk chunk = world().getChunkAt(player().getLocation().getBlockZ() / 16, player().getLocation().getBlockZ() / 16);
IrisRegion r = engine().getRegion(chunk);
var loc = player().getLocation();
IrisRegion r = engine().getRegion(loc);
sender().sendMessage("IRegion: " + r.getLoadKey() + " (" + r.getName() + ")");
} catch (Throwable e) {
@@ -98,7 +98,7 @@ public class CommandWhat implements DecreeExecutor {
}
}
@Decree(description = "What block am i looking at?", origin = DecreeOrigin.PLAYER)
@Decree(description = "What block am i looking at?", origin = DecreeOrigin.PLAYER, sync = true)
public void block() {
BlockData bd;
try {
@@ -18,9 +18,10 @@
package com.volmit.iris.core.edit;
import com.volmit.iris.Iris;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.AR;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.SR;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@@ -79,7 +80,7 @@ public class BlockSignal {
e.setTicksLived(1);
e.setVelocity(new Vector(0, 0, 0));
new SR(20) {
new AR(20) {
@Override
public void run() {
if (e.isDead()) {
@@ -87,9 +88,10 @@ public class BlockSignal {
return;
}
e.setTicksLived(1);
e.teleport(tg.clone());
e.setVelocity(new Vector(0, 0, 0));
Iris.platform.teleportAsync(e, tg.clone()).thenAccept(b -> {
e.setTicksLived(1);
e.setVelocity(new Vector(0, 0, 0));
}).join();
}
};
@@ -30,6 +30,7 @@ import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import de.crazydev22.platformutils.scheduler.task.Task;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
@@ -55,7 +56,7 @@ public class JigsawEditor implements Listener {
private final IrisJigsawPiece piece;
private final Location origin;
private final Cuboid cuboid;
private final int ticker;
private final Task ticker;
private final KMap<IrisPosition, Runnable> falling = new KMap<>();
private final ChronoLatch cl = new ChronoLatch(100);
private Location target;
@@ -106,21 +107,26 @@ public class JigsawEditor implements Listener {
}
public Location toLocation(IrisPosition i) {
return origin.clone()
return toBlock(origin.clone()
.add(new Vector(i.getX(), i.getY(), i.getZ()))
.add(object.getCenter())
.getBlock()
.getLocation();
.add(object.getCenter()));
}
public IrisPosition toPosition(Location l) {
return new IrisPosition(l.clone().getBlock().getLocation()
return new IrisPosition(l.clone()
.subtract(origin.clone())
.subtract(object.getCenter())
.add(1, 1, 1)
.toVector());
}
private Location toBlock(Location location) {
location.setX(Math.floor(location.getX()));
location.setY(Math.floor(location.getY()));
location.setZ(Math.floor(location.getZ()));
return location;
}
@EventHandler
public void on(PlayerInteractEvent e) {
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
@@ -197,7 +203,7 @@ public class JigsawEditor implements Listener {
}
public void exit() {
J.car(ticker);
if (ticker != null) ticker.cancel();
Iris.instance.unregisterListener(this);
try {
J.sfut(() -> {
@@ -763,7 +763,7 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
int xx = (int) getWorldX(hx);
int zz = (int) getWorldZ(hz);
int h = engine.getComplex().getRoundedHeighteightStream().get(xx, zz);
player.teleport(new Location(player.getWorld(), xx, h, zz));
Iris.platform.teleportAsync(player, new Location(player.getWorld(), xx, h, zz));
notify("Teleporting to " + xx + ", " + h + ", " + zz);
} else {
notify("No player in world, can't teleport.");
@@ -0,0 +1,68 @@
package com.volmit.iris.core.link;
import lombok.NonNull;
import net.thenextlvl.worlds.api.WorldsProvider;
import net.thenextlvl.worlds.api.generator.GeneratorType;
import net.thenextlvl.worlds.api.generator.LevelStem;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.concurrent.CompletableFuture;
public class FoliaWorldsLink {
private static FoliaWorldsLink instance;
private final Object provider;
private FoliaWorldsLink(Object provider) {
this.provider = provider;
}
public static FoliaWorldsLink get() {
if(instance == null) {
synchronized (FoliaWorldsLink.class) {
try {
Server.class.getDeclaredMethod("isGlobalTickThread");
instance = new FoliaWorldsLink(Bukkit.getServicesManager().load(WorldsProvider.class));
} catch (Throwable e) {
instance = new FoliaWorldsLink(null);
}
}
}
return instance;
}
public boolean isActive() {
return provider != null;
}
@Nullable
public CompletableFuture<World> createWorld(@NonNull WorldCreator creator) {
if (provider == null) return null;
return ((WorldsProvider) provider)
.levelBuilder(new File(Bukkit.getWorldContainer(), creator.name()).toPath())
.name(creator.name())
.seed(creator.seed())
.levelStem(switch (creator.environment()) {
case CUSTOM, NORMAL -> LevelStem.OVERWORLD;
case NETHER -> LevelStem.NETHER;
case THE_END -> LevelStem.END;
})
.chunkGenerator(creator.generator())
.biomeProvider(creator.biomeProvider())
.generatorType(switch (creator.type()) {
case NORMAL -> GeneratorType.NORMAL;
case FLAT -> GeneratorType.FLAT;
case LARGE_BIOMES -> GeneratorType.LARGE_BIOMES;
case AMPLIFIED -> GeneratorType.AMPLIFIED;
})
.structures(creator.generateStructures())
.hardcore(creator.hardcore())
.build()
.createAsync();
}
}
@@ -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 IrisCustomData.of(blockData, ExternalDataSVC.buildState(blockId, state));
return new IrisCustomData(blockData, ExternalDataSVC.buildState(blockId, state));
}
@NotNull
@@ -47,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
if (block == null) {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
return IrisCustomData.of(block.getBaseBlockData(), blockId);
return new IrisCustomData(block.getBaseBlockData(), blockId);
}
@NotNull
@@ -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 IrisCustomData.of(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state));
return new IrisCustomData(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 IrisCustomData.of(B.getAir(), ExternalDataSVC.buildState(blockId, state));
return new IrisCustomData(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 IrisCustomData.of(data, blockState);
return new IrisCustomData(data, blockState);
} else if (NexoFurniture.isFurniture(blockId.key())) {
return IrisCustomData.of(B.getAir(), blockState);
return new IrisCustomData(B.getAir(), blockState);
}
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
@@ -50,15 +50,16 @@ import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.Objects;
import java.util.Optional;
@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;
@@ -91,6 +92,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.engine = null;
this.dataFolder = dataFolder;
this.id = RNG.r.imax();
this.environment = PackEnvironment.create(this);
hotloaded();
}
@@ -348,6 +350,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
public synchronized void hotloaded() {
closed = false;
environment.close();
possibleSnippets = new KMap<>();
builder = new GsonBuilder()
.addDeserializationExclusionStrategy(this)
@@ -379,7 +382,6 @@ 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();
@@ -387,10 +389,6 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
.map(IrisDimension::getDataScripts)
.flatMap(KList::stream)
.forEach(environment::execute);
if (engine != null) {
engine.hotload();
}
}
public void dump() {
@@ -406,33 +404,6 @@ 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 'object' and modify properties about this object before it's used.\nFile extension: .proc.kts")
@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.")
@RegistryListResource(IrisScript.class)
@ArrayType(min = 1, type = String.class)
private KList<String> preprocessors = new KList<>();
@@ -46,7 +46,6 @@ 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;
@@ -171,6 +170,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return possibleKeys;
}
KSet<String> m = new KSet<>();
KList<File> files = getFolders();
if (files == null) {
@@ -178,7 +178,6 @@ 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", ""));
@@ -320,8 +319,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return null;
}
var set = firstAccess;
if (set != null) firstAccess.add(name);
firstAccess.add(name);
return loadCache.get(name);
}
@@ -344,24 +342,21 @@ 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);
var set = firstAccess;
firstAccess = null;
dos.writeInt(set.size());
dos.writeInt(firstAccess.size());
for (String i : set) {
for (String i : firstAccess) {
dos.writeUTF(i);
}
@@ -28,10 +28,9 @@ 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, 10, null);
new Version(21, 8, null);
private static final List<Version> REVISION = List.of(
new Version(21, 9, "v1_21_R6"),
new Version(21, 6, "v1_21_R5"),
new Version(21, 5, "v1_21_R4"),
new Version(21, 4, "v1_21_R3"),
@@ -20,6 +20,7 @@ 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.link.FoliaWorldsLink;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.container.Pair;
@@ -43,6 +44,7 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import java.awt.Color;
import java.util.concurrent.CompletableFuture;
import java.util.List;
public interface INMSBinding {
@@ -101,6 +103,15 @@ public interface INMSBinding {
return c.createWorld();
}
default CompletableFuture<World> createWorldAsync(WorldCreator c) {
try {
var link = FoliaWorldsLink.get();
return link.isActive() ? link.createWorld(c) : CompletableFuture.completedFuture(createWorld(c));
} catch (Throwable e) {
return CompletableFuture.failedFuture(e);
}
}
int countCustomBiomes();
void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk);
@@ -2,24 +2,25 @@ package com.volmit.iris.core.pregenerator;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.core.nms.container.Pair;
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;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -29,7 +30,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 TicketHolder holder;
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
private final RollingSequence chunksPerSecond = new RollingSequence(5);
private final AtomicInteger totalMaxChunks = new AtomicInteger();
private final AtomicInteger chunksProcessed = new AtomicInteger();
@@ -38,13 +39,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 chunkExecutor = IrisSettings.get().getUpdater().isNativeThreads() ? Executors.newFixedThreadPool(coreLimit) : Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final CountDownLatch latch;
private final Engine engine;
@@ -53,7 +54,6 @@ 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);
@@ -112,6 +112,7 @@ 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)
@@ -126,7 +127,6 @@ public class ChunkUpdater {
t.setPriority(Thread.MAX_PRIORITY);
t.start();
Iris.service(PreservationSVC.class).register(t);
} catch (Exception e) {
e.printStackTrace();
}
@@ -139,6 +139,8 @@ public class ChunkUpdater {
chunkExecutor.shutdown();
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
scheduler.shutdownNow();
unloadAndSaveAllChunks();
} catch (Exception ignored) {}
@@ -197,16 +199,20 @@ 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);
removeTickets(x, z);
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();
}
}
} finally {
chunksUpdated.incrementAndGet();
chunksProcessed.getAndIncrement();
mc.release();
}
}
@@ -216,7 +222,7 @@ public class ChunkUpdater {
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (!PaperLib.isChunkGenerated(world, x + dx, z + dz)) {
if (!Iris.platform.isChunkGenerated(world, x + dx, z + dz)) {
return false;
}
}
@@ -228,16 +234,41 @@ public class ChunkUpdater {
for (int dz = -1; dz <= 1; dz++) {
int xx = x + dx;
int zz = z + dz;
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();
});
executor.submit(() -> {
try {
Chunk c;
try {
c = Iris.platform.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 (!Iris.platform.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();
}
});
}
}
@@ -246,16 +277,27 @@ public class ChunkUpdater {
} catch (InterruptedException e) {
Iris.info("Interrupted while waiting for chunks to load");
}
if (generated.get()) return true;
removeTickets(x, z);
return false;
return generated.get();
}
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);
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);
});
}
}
}
@@ -268,6 +310,7 @@ public class ChunkUpdater {
return;
}
unloadChunks();
world.save();
}).get();
} catch (Throwable e) {
@@ -22,7 +22,6 @@ import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.FileWriter;
@@ -235,16 +234,13 @@ public class DeepSearchPregenerator extends Thread implements Listener {
}
save();
jobs.remove(world.getName());
new BukkitRunnable() {
@Override
public void run() {
while (deepFile.exists()){
deepFile.delete();
J.sleep(1000);
}
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
J.a(() -> {
while (deepFile.exists()) {
deepFile.delete();
J.sleep(1000);
}
}.runTaskLater(Iris.instance, 20L);
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
}, 10);
} catch (Exception e) {
Iris.error("Failed to shutdown DeepSearch for " + world.getName());
e.printStackTrace();
@@ -11,7 +11,6 @@ import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import io.papermc.lib.PaperLib;
import lombok.Data;
import lombok.Getter;
import org.bukkit.Bukkit;
@@ -19,7 +18,6 @@ import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
@@ -149,23 +147,9 @@ public class LazyPregenerator extends Thread implements Listener {
private void tickGenerate(Position2 chunk) {
executorService.submit(() -> {
CountDownLatch latch = new CountDownLatch(1);
if (PaperLib.isPaper()) {
PaperLib.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true)
.thenAccept((i) -> {
Iris.verbose("Generated Async " + chunk);
latch.countDown();
});
} else {
J.s(() -> {
world.getChunkAt(chunk.getX(), chunk.getZ());
Iris.verbose("Generated " + chunk);
latch.countDown();
});
}
try {
latch.await();
} catch (InterruptedException ignored) {}
Iris.platform.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true).thenAccept((i) -> {
Iris.verbose("Generated Async " + chunk);
}).join();
lazyGeneratedChunks.addAndGet(1);
});
}
@@ -238,16 +222,13 @@ public class LazyPregenerator extends Thread implements Listener {
}
save();
jobs.remove(world.getName());
new BukkitRunnable() {
@Override
public void run() {
while (lazyFile.exists()){
lazyFile.delete();
J.sleep(1000);
}
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
J.a(() -> {
while (lazyFile.exists()){
lazyFile.delete();
J.sleep(1000);
}
}.runTaskLater(Iris.instance, 20L);
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
}, 20);
} catch (Exception e) {
Iris.error("Failed to shutdown Lazygen for " + world.getName());
e.printStackTrace();
@@ -17,7 +17,6 @@ import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import io.papermc.lib.PaperLib;
import lombok.Data;
import lombok.Getter;
import org.apache.logging.log4j.core.util.ExecutorServices;
@@ -26,7 +25,6 @@ import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.checkerframework.checker.units.qual.N;
import java.io.File;
@@ -227,7 +225,7 @@ public class TurboPregenerator extends Thread implements Listener {
private void tickGenerate(Position2 chunk) {
executorService.submit(() -> {
CountDownLatch latch = new CountDownLatch(1);
PaperLib.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true)
Iris.platform.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true)
.thenAccept((i) -> {
latch.countDown();
});
@@ -302,16 +300,13 @@ public class TurboPregenerator extends Thread implements Listener {
}
save();
jobs.remove(world.getName());
new BukkitRunnable() {
@Override
public void run() {
while (turboFile.exists()) {
turboFile.delete();
J.sleep(1000);
}
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
J.a(() -> {
while (turboFile.exists()) {
turboFile.delete();
J.sleep(1000);
}
}.runTaskLater(Iris.instance, 20L);
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
}, 20);
} catch (Exception e) {
Iris.error("Failed to shutdown turbogen for " + world.getName());
e.printStackTrace();
@@ -24,11 +24,9 @@ public interface PregenCache {
void write();
void trim(long unloadDuration);
static PregenCache create(File directory) {
if (directory == null) return EMPTY;
return new PregenCacheImpl(directory, 16);
return new PregenCacheImpl(directory);
}
default PregenCache sync() {
@@ -53,16 +51,19 @@ 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) {}
}
};
@@ -0,0 +1,220 @@
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,6 +1,11 @@
package com.volmit.iris.core.pregenerator.cache;
record SynchronizedCache(PregenCache cache) implements PregenCache {
import lombok.AllArgsConstructor;
@AllArgsConstructor
class SynchronizedCache implements PregenCache {
private final PregenCache cache;
@Override
public boolean isThreadSafe() {
return true;
@@ -40,11 +45,4 @@ record SynchronizedCache(PregenCache cache) implements PregenCache {
cache.write();
}
}
@Override
public void trim(long unloadDuration) {
synchronized (cache) {
cache.trim(unloadDuration);
}
}
}
@@ -1,73 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.util.mantle.Mantle;
import io.papermc.lib.PaperLib;
import org.bukkit.World;
public class AsyncOrMedievalPregenMethod implements PregeneratorMethod {
private final PregeneratorMethod method;
public AsyncOrMedievalPregenMethod(World world, int threads) {
method = PaperLib.isPaper() ? new AsyncPregenMethod(world, threads) : new MedievalPregenMethod(world);
}
@Override
public void init() {
method.init();
}
@Override
public void close() {
method.close();
}
@Override
public void save() {
method.save();
}
@Override
public String getMethod(int x, int z) {
return method.getMethod(x, z);
}
@Override
public boolean supportsRegions(int x, int z, PregenListener listener) {
return false;
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
throw new UnsupportedOperationException();
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
method.generateChunk(x, z, listener);
}
@Override
public Mantle getMantle() {
return method.getMantle();
}
}
@@ -23,17 +23,17 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.J;
import io.papermc.lib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@@ -48,11 +48,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private final boolean urgent;
private final Map<Chunk, Long> lastUse;
public AsyncPregenMethod(World world, int unusedThreads) {
if (!PaperLib.isPaper()) {
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
}
public AsyncPregenMethod(World world) {
this.world = world;
this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor();
this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
@@ -63,26 +59,28 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private void unloadAndSaveAllChunks() {
try {
J.sfut(() -> {
if (world == null) {
Iris.warn("World was null somehow...");
return;
}
if (world == null) {
Iris.warn("World was null somehow...");
return;
}
long minTime = M.ms() - 10_000;
lastUse.entrySet().removeIf(i -> {
final Chunk chunk = i.getKey();
final Long lastUseTime = i.getValue();
if (!chunk.isLoaded() || lastUseTime == null)
return true;
if (lastUseTime < minTime) {
chunk.unload();
return true;
}
return false;
});
world.save();
}).get();
long minTime = M.ms() - 10_000;
KList<CompletableFuture<?>> futures = new KList<>();
lastUse.entrySet().removeIf(i -> {
final Chunk chunk = i.getKey();
final Long lastUseTime = i.getValue();
if (!chunk.isLoaded() || lastUseTime == null)
return true;
if (lastUseTime < minTime) {
futures.add(Iris.platform.getRegionScheduler()
.run(chunk.getWorld(), chunk.getX(), chunk.getZ(), () -> chunk.unload())
.getResult());
return true;
}
return false;
});
futures.add(Iris.platform.getRegionScheduler().run(world, 0, 0, world::save).getResult());
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
} catch (Throwable e) {
e.printStackTrace();
}
@@ -197,7 +195,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
public void generate(int x, int z, PregenListener listener) {
service.submit(() -> {
try {
PaperLib.getChunkAtAsync(world, x, z, true, urgent).thenAccept((i) -> {
Iris.platform.getChunkAtAsync(world, x, z, true, urgent).thenAccept((i) -> {
listener.onChunkGenerated(x, z);
listener.onChunkCleaned(x, z);
if (i == null) return;
@@ -222,7 +220,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private class TicketExecutor implements Executor {
@Override
public void generate(int x, int z, PregenListener listener) {
PaperLib.getChunkAtAsync(world, x, z, true, urgent)
Iris.platform.getChunkAtAsync(world, x, z, true, urgent)
.exceptionally(e -> {
Iris.reportError(e);
e.printStackTrace();
@@ -1,74 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.util.mantle.Mantle;
import org.bukkit.World;
public class HybridPregenMethod implements PregeneratorMethod {
private final PregeneratorMethod inWorld;
private final World world;
public HybridPregenMethod(World world, int threads) {
this.world = world;
inWorld = new AsyncOrMedievalPregenMethod(world, threads);
}
@Override
public String getMethod(int x, int z) {
return "Hybrid<" + inWorld.getMethod(x, z) + ">";
}
@Override
public void init() {
inWorld.init();
}
@Override
public void close() {
inWorld.close();
}
@Override
public void save() {
inWorld.save();
}
@Override
public boolean supportsRegions(int x, int z, PregenListener listener) {
return inWorld.supportsRegions(x, z, listener);
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
inWorld.generateRegion(x, z, listener);
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
inWorld.generateChunk(x, z, listener);
}
@Override
public Mantle getMantle() {
return inWorld.getMantle();
}
}
@@ -1,138 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.J;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class MedievalPregenMethod implements PregeneratorMethod {
private final World world;
private final KList<CompletableFuture<?>> futures;
private final Map<Chunk, Long> lastUse;
public MedievalPregenMethod(World world) {
this.world = world;
futures = new KList<>();
this.lastUse = new KMap<>();
}
private void waitForChunks() {
for (CompletableFuture<?> i : futures) {
try {
i.get();
} catch (Throwable e) {
e.printStackTrace();
}
}
futures.clear();
}
private void unloadAndSaveAllChunks() {
try {
J.sfut(() -> {
if (world == null) {
Iris.warn("World was null somehow...");
return;
}
for (Chunk i : new ArrayList<>(lastUse.keySet())) {
Long lastUseTime = lastUse.get(i);
if (lastUseTime != null && M.ms() - lastUseTime >= 10) {
i.unload();
lastUse.remove(i);
}
}
world.save();
}).get();
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void init() {
unloadAndSaveAllChunks();
}
@Override
public void close() {
unloadAndSaveAllChunks();
}
@Override
public void save() {
unloadAndSaveAllChunks();
}
@Override
public boolean supportsRegions(int x, int z, PregenListener listener) {
return false;
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
throw new UnsupportedOperationException();
}
@Override
public String getMethod(int x, int z) {
return "Medieval";
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
if (futures.size() > IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())) {
waitForChunks();
}
listener.onChunkGenerating(x, z);
futures.add(J.sfut(() -> {
world.getChunkAt(x, z);
Chunk c = Bukkit.getWorld(world.getUID()).getChunkAt(x, z);
lastUse.put(c, M.ms());
listener.onChunkGenerated(x, z);
listener.onChunkCleaned(x, z);
}));
}
@Override
public Mantle getMantle() {
if (IrisToolbelt.isIrisWorld(world)) {
return IrisToolbelt.access(world).getEngine().getMantle().getMantle();
}
return null;
}
}
@@ -9,7 +9,9 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.*;
import java.util.HashMap;
import java.util.Optional;
import java.util.Scanner;
public class Gradle {
private static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
@@ -36,15 +38,10 @@ public class Gradle {
cmd[0] = gradle.getAbsolutePath();
System.arraycopy(args, 0, cmd, 1, args.length);
var process = Runtime.getRuntime().exec(cmd, ENVIRONMENT, projectDir);
var lines = Collections.synchronizedList(new ArrayList<String>());
attach(process.getInputStream(), lines);
attach(process.getErrorStream(), lines);
attach(process.getInputStream());
attach(process.getErrorStream());
var code = process.waitFor();
if (code == 0) {
lines.forEach(Iris::debug);
return;
}
lines.forEach(Iris::error);
if (code == 0) return;
throw new RuntimeException("Gradle exited with code " + code);
}
@@ -94,12 +91,12 @@ public class Gradle {
.orElseThrow(() -> new RuntimeException("Failed to find java home, please set java.home system property"));
}
private static void attach(InputStream stream, List<String> list) {
Thread.ofPlatform().start(() -> {
private static void attach(InputStream stream) {
Thread.ofVirtual().start(() -> {
try (var in = new Scanner(stream)) {
while (in.hasNextLine()) {
String line = in.nextLine();
list.add(line);
Iris.debug("[GRADLE] " + line);
}
}
});
@@ -24,6 +24,7 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.scripting.environment.SimpleEnvironment;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.object.annotations.Snippet;
@@ -325,7 +326,7 @@ public class IrisProject {
}
}
for (Class<?> i : dm.resolveSnippets()) {
for (Class<?> i : Iris.getClasses("com.volmit.iris.engine.object.", Snippet.class)) {
try {
String snipType = i.getDeclaredAnnotation(Snippet.class).value();
JSONObject o = new JSONObject();
@@ -38,7 +38,6 @@ 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;
@@ -118,13 +117,49 @@ public class SchemaBuilder {
JSONArray required = new JSONArray();
JSONArray extended = new JSONArray();
var parent = c.getSuperclass();
while (parent != null && IrisRegistrant.class.isAssignableFrom(parent)) {
buildProperties(properties, required, extended, parent);
parent = parent.getSuperclass();
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);
}
}
buildProperties(properties, required, extended, c);
for (Field k : c.getDeclaredFields()) {
k.setAccessible(true);
if (Modifier.isStatic(k.getModifiers()) || Modifier.isFinal(k.getModifiers()) || Modifier.isTransient(k.getModifiers())) {
continue;
}
JSONObject property = buildProperty(k, c);
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
if (required.length() > 0) {
o.put("required", required);
@@ -139,33 +174,6 @@ 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());
@@ -608,7 +616,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 (!k.getType().isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(value instanceof Enum<?>) && !KeyedType.isKeyed(k.getType())) {
} else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !KeyedType.isKeyed(cl)) {
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,12 +1,9 @@
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;
@@ -25,6 +22,4 @@ 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,7 +2,6 @@ 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;
@@ -17,6 +16,4 @@ public interface PackEnvironment extends SimpleEnvironment {
@Nullable
Object createNoise(@NonNull String script, @NonNull RNG rng);
EngineEnvironment with(@NonNull Engine engine);
}
@@ -27,4 +27,8 @@ public interface SimpleEnvironment {
@Nullable
Object evaluate(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
default void close() {
}
}
@@ -1,22 +0,0 @@
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();
}
}
@@ -24,7 +24,6 @@ import com.volmit.iris.core.edit.BukkitBlockEditor;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.plugin.IrisService;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
@@ -38,7 +37,7 @@ public class EditSVC implements IrisService {
@Override
public void onEnable() {
this.editors = new KMap<>();
Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::update, 1000, 1000);
Iris.platform.getGlobalScheduler().runAtFixedRate(this::update, 1000, 1000);
}
@Override
@@ -1,11 +1,13 @@
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;
@@ -17,33 +19,21 @@ 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 KMap<String, Reference<PregenCache>> REFERENCE_CACHE = new KMap<>();
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder()
.executor(KCache.EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.weakValues()
.build();
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);
@@ -52,9 +42,6 @@ public class GlobalCacheSVC implements IrisService {
@Override
public void onDisable() {
disabled = true;
try {
trimmer.join();
} catch (InterruptedException ignored) {}
globalCache.qclear((world, cache) -> cache.write());
}
@@ -89,7 +76,6 @@ public class GlobalCacheSVC implements IrisService {
}
private void createCache(World world) {
if (!IrisToolbelt.isIrisWorld(world)) return;
globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault);
}
@@ -113,15 +99,7 @@ public class GlobalCacheSVC implements IrisService {
@NonNull
public static PregenCache createCache(@NonNull String worldName, @NonNull Function<String, PregenCache> 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];
return REFERENCE_CACHE.get(worldName, provider);
}
@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.ResourceLoader;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
@@ -14,8 +14,6 @@ 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;
@@ -29,7 +27,6 @@ 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();
@@ -67,26 +64,10 @@ 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:");
@@ -95,14 +76,10 @@ 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 + "-------------------------");
}
@@ -136,12 +113,12 @@ public class IrisEngineSVC implements IrisService {
@Override
protected long loop() {
try {
int queuedPlates = 0;
int totalPlates = 0;
long chunks = 0;
int unloaders = 0;
int trimmers = 0;
int iris = 0;
queuedTectonicPlates.set(0);
tectonicPlates.set(0);
loadedChunks.set(0);
unloaderAlive.set(0);
trimmerAlive.set(0);
totalWorlds.set(0);
double maxDuration = Long.MIN_VALUE;
double minDuration = Long.MAX_VALUE;
@@ -149,30 +126,23 @@ public class IrisEngineSVC implements IrisService {
var registered = entry.getValue();
if (registered.closed) continue;
iris++;
if (registered.unloaderAlive()) unloaders++;
if (registered.trimmerAlive()) trimmers++;
totalWorlds.incrementAndGet();
unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0);
trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0);
var engine = registered.getEngine();
if (engine == null) continue;
queuedPlates += engine.getMantle().getUnloadRegionCount();
totalPlates += engine.getMantle().getLoadedRegionCount();
chunks += entry.getKey().getLoadedChunks().length;
queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount());
tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
loadedChunks.addAndGet(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) {
@@ -187,7 +157,7 @@ public class IrisEngineSVC implements IrisService {
private final class Registered {
private final String name;
private final PlatformChunkGenerator access;
private final int offset = RNG.r.nextInt(TRIM_PERIOD);
private final int offset = RNG.r.nextInt(1000);
private transient ScheduledFuture<?> trimmer;
private transient ScheduledFuture<?> unloader;
private transient boolean closed;
@@ -224,7 +194,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to trim for " + name);
e.printStackTrace();
}
}, offset, TRIM_PERIOD, TimeUnit.MILLISECONDS);
}, offset, 2000, TimeUnit.MILLISECONDS);
}
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
@@ -244,7 +214,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to unload for " + name);
e.printStackTrace();
}
}, offset + TRIM_PERIOD / 2, TRIM_PERIOD, TimeUnit.MILLISECONDS);
}, offset + 1000, 2000, TimeUnit.MILLISECONDS);
}
}
@@ -22,7 +22,6 @@ import com.volmit.iris.Iris;
import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.scheduling.J;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@@ -71,21 +70,18 @@ public class ObjectSVC implements IrisService {
*/
private void revert(Map<Block, BlockData> blocks) {
Iterator<Map.Entry<Block, BlockData>> it = blocks.entrySet().iterator();
Bukkit.getScheduler().runTask(Iris.instance, () -> {
int amount = 0;
while (it.hasNext()) {
Map.Entry<Block, BlockData> entry = it.next();
BlockData data = entry.getValue();
entry.getKey().setBlockData(data, false);
var scheduler = Iris.platform.getRegionScheduler();
for (int i = 0; i < 200 && it.hasNext(); i++) {
Map.Entry<Block, BlockData> entry = it.next();
Block block = entry.getKey();
BlockData data = entry.getValue();
it.remove();
it.remove();
scheduler.run(block.getLocation(), () -> block.setBlockData(data, false));
}
amount++;
if (amount > 200) {
J.s(() -> revert(blocks), 1);
}
}
});
if (it.hasNext()) {
J.s(() -> revert(blocks), 1);
}
}
}
@@ -24,22 +24,19 @@ 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<WeakReference<MeteredCache>> caches = new CopyOnWriteArrayList<>();
private final List<MeteredCache> caches = new CopyOnWriteArrayList<>();
private Looper dereferencer;
public void register(Thread t) {
@@ -51,18 +48,22 @@ public class PreservationSVC implements IrisService {
}
public void printCaches() {
var c = getCaches();
long s = 0;
long m = 0;
long s = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getSize).sum();
long m = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getMaxSize).sum();
double p = 0;
double mf = Math.max(c.size(), 1);
double mf = 0;
for (MeteredCache i : c) {
s += i.getSize();
m += i.getMaxSize();
for (MeteredCache i : caches) {
if (i.isClosed()) {
continue;
}
mf++;
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");
}
@@ -118,29 +119,14 @@ public class PreservationSVC implements IrisService {
}
public void updateCaches() {
caches.removeIf(ref -> {
var c = ref.get();
return c == null || c.isClosed();
});
caches.removeIf(MeteredCache::isClosed);
}
public void registerCache(MeteredCache cache) {
caches.add(new WeakReference<>(cache));
caches.add(cache);
}
public List<KCache<?, ?>> caches() {
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());
return caches.stream().map(MeteredCache::getRawCache).collect(Collectors.toList());
}
}
@@ -142,9 +142,10 @@ public class TreeSVC implements IrisService {
public void set(int x, int y, int z, BlockData d) {
Block b = event.getWorld().getBlockAt(x, y, z);
BlockState state = b.getState();
if (d instanceof IrisCustomData data)
if (d instanceof IrisCustomData data) {
state.setBlockData(data.getBase());
else state.setBlockData(d);
Iris.service(ExternalDataSVC.class).processUpdate(engine, b, data.getCustom());
} else state.setBlockData(d);
blockStateList.add(b.getState());
dataCache.put(new Location(event.getWorld(), x, y, z), d);
}
@@ -189,16 +190,6 @@ 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;
@@ -217,8 +208,7 @@ public class TreeSVC implements IrisService {
event.setCancelled(true);
J.s(() -> {
Iris.platform.getRegionScheduler().run(event.getLocation(), () -> {
StructureGrowEvent iGrow = new StructureGrowEvent(event.getLocation(), event.getSpecies(), event.isFromBonemeal(), event.getPlayer(), blockStateList);
block = true;
Bukkit.getServer().getPluginManager().callEvent(iGrow);
@@ -235,7 +225,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, false);
} else block.setBlockData(d);
}
}
});
@@ -35,6 +35,7 @@ import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.SR;
import com.volmit.iris.util.scheduling.jobs.Job;
import com.volmit.iris.util.scheduling.jobs.ScanJob;
import org.bukkit.*;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
@@ -86,82 +87,10 @@ public class WandSVC implements IrisService {
Cuboid c = new Cuboid(f[0], f[1]);
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ());
var it = c.chunkedIterator();
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;
@Override
public String getName() {
return "Scanning Selection";
}
@Override
public void execute() {
new SR() {
@Override
public void run() {
var time = M.ms() + MS_PER_TICK;
while (time > M.ms()) {
if (!it.hasNext()) {
if (chunk != null) {
holder.removeTicket(chunk);
chunk = null;
}
cancel();
latch.countDown();
return;
}
try {
var b = it.next();
var bChunk = b.getChunk();
if (chunk == null) {
chunk = bChunk;
holder.addTicket(chunk);
} else if (chunk != bChunk) {
holder.removeTicket(chunk);
holder.addTicket(bChunk);
chunk = bChunk;
}
if (b.getType().equals(Material.AIR))
continue;
BlockVector bv = b.getLocation().subtract(c.getLowerNE().toVector()).toVector().toBlockVector();
s.setUnsigned(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ(), b, legacy);
} finally {
i++;
}
}
}
};
try {
latch.await();
} catch (InterruptedException ignored) {}
}
@Override
public void completeWork() {}
@Override
public int getTotalWork() {
return total;
}
@Override
public int getWorkCompleted() {
return i;
}
}.execute(new VolmitSender(p), true, () -> {});
try {
latch.await();
} catch (InterruptedException ignored) {}
new ScanJob("Scanning Selection", c, MS_PER_TICK, (bv, b) -> {
if (b.getType().equals(Material.AIR)) return;
s.setUnsigned(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ(), b, legacy);
}).execute(new VolmitSender(p), true, () -> {});
return s;
} catch (Throwable e) {
@@ -11,6 +11,7 @@ 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 de.crazydev22.platformutils.scheduler.task.Task;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.apache.commons.io.FileUtils;
import org.bukkit.Bukkit;
@@ -58,7 +59,7 @@ public class IrisConverter {
int objW = ((ShortTag) compound.get("Width")).getValue();
int objH = ((ShortTag) compound.get("Height")).getValue();
int objD = ((ShortTag) compound.get("Length")).getValue();
int i = -1;
Task i = null;
int mv = objW * objH * objD;
AtomicInteger v = new AtomicInteger(0);
if (mv > 500_000) {
@@ -66,9 +67,7 @@ public class IrisConverter {
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(() -> {
sender.sendProgress((double) v.get() / mv, "Converting");
}, 0);
i = J.ar(() -> sender.sendProgress((double) v.get() / mv, "Converting"), 0);
}
}
@@ -89,7 +88,7 @@ public class IrisConverter {
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(Byte.toUnsignedInt(originalBlockArray[v.get()]));
BlockData bd = blockmap.get((int) originalBlockArray[v.get()]);
if (!bd.getMaterial().isAir()) {
object.setUnsigned(w, h, d, bd);
}
@@ -97,7 +96,7 @@ public class IrisConverter {
}
}
}
if (i != -1) J.car(i);
if (i != null) i.cancel();
try {
object.shrinkwrap();
object.write(new File(folder, schem.getName().replace(".schem", ".iob")));
@@ -32,7 +32,6 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.O;
import lombok.Data;
import lombok.experimental.Accessors;
import org.bukkit.*;
@@ -41,8 +40,10 @@ import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.IntSupplier;
import static com.volmit.iris.util.misc.ServerProperties.BUKKIT_YML;
@@ -128,8 +129,6 @@ public class IrisCreator {
}
AtomicDouble pp = new AtomicDouble(0);
O<Boolean> done = new O<>();
done.set(false);
WorldCreator wc = new IrisWorldCreator()
.dimension(dimension)
.name(name)
@@ -143,6 +142,7 @@ public class IrisCreator {
PlatformChunkGenerator access = (PlatformChunkGenerator) wc.generator();
if (access == null) throw new IrisException("Access is null. Something bad happened.");
AtomicBoolean done = new AtomicBoolean(false);
J.a(() -> {
IntSupplier g = () -> {
if (access.getEngine() == null) {
@@ -166,9 +166,11 @@ public class IrisCreator {
});
World world;
final World world;
try {
world = J.sfut(() -> INMS.get().createWorld(wc)).get();
world = J.sfut(() -> INMS.get().createWorldAsync(wc))
.thenCompose(Function.identity())
.get();
} catch (Throwable e) {
done.set(true);
throw new IrisException("Failed to create world!", e);
@@ -177,7 +179,15 @@ public class IrisCreator {
done.set(true);
if (sender.isPlayer() && !benchmark) {
J.s(() -> sender.player().teleport(new Location(world, 0, world.getHighestBlockYAt(0, 0) + 1, 0)));
Iris.platform.getChunkAtAsync(world, 0, 0, true, true)
.thenApply(Objects::requireNonNull)
.thenApply(c -> c.getChunkSnapshot(true, false, false).getHighestBlockYAt(0, 0) + 1)
.thenAccept(y -> Iris.platform.teleportAsync(sender.player(), new Location(world, 0, y, 0)))
.exceptionally(err -> {
sender.sendMessage(C.RED + "Failed to teleport you to the world!");
err.printStackTrace();
return null;
});
}
if (studio || benchmark) {
@@ -24,8 +24,8 @@ import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.pregenerator.methods.AsyncPregenMethod;
import com.volmit.iris.core.pregenerator.methods.CachedPregenMethod;
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension;
@@ -34,7 +34,6 @@ 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;
@@ -46,7 +45,6 @@ import java.util.Map;
* Hope you packed snacks & road sodas.
*/
public class IrisToolbelt {
@ApiStatus.Internal
public static Map<String, Boolean> toolbeltConfiguration = new HashMap<>();
/**
@@ -167,8 +165,7 @@ public class IrisToolbelt {
* @return the pregenerator job (already started)
*/
public static PregeneratorJob pregenerate(PregenTask task, PlatformChunkGenerator gen) {
return pregenerate(task, new HybridPregenMethod(gen.getEngine().getWorld().realWorld(),
IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), gen.getEngine());
return pregenerate(task, new AsyncPregenMethod(gen.getEngine().getWorld().realWorld()), gen.getEngine());
}
/**
@@ -184,7 +181,7 @@ public class IrisToolbelt {
return pregenerate(task, access(world));
}
return pregenerate(task, new HybridPregenMethod(world, IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), null);
return pregenerate(task, new AsyncPregenMethod(world), null);
}
/**
@@ -198,7 +195,7 @@ public class IrisToolbelt {
if (!i.getName().equals(world.getName())) {
for (Player j : world.getPlayers()) {
new VolmitSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world.");
j.teleport(i.getSpawnLocation());
Iris.platform.teleportAsync(j, i.getSpawnLocation());
}
return true;
@@ -220,7 +217,7 @@ public class IrisToolbelt {
if (!i.getName().equals(world.getName())) {
for (Player j : world.getPlayers()) {
new VolmitSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world. " + m);
j.teleport(i.getSpawnLocation());
Iris.platform.teleportAsync(j, i.getSpawnLocation());
}
return true;
}
@@ -234,11 +231,7 @@ public class IrisToolbelt {
}
public static void retainMantleDataForSlice(String className) {
toolbeltConfiguration.put("retain.mantle." + className, Boolean.TRUE);
}
public static boolean isRetainingMantleDataForSlice(String className) {
return !toolbeltConfiguration.isEmpty() && toolbeltConfiguration.get("retain.mantle." + className) == Boolean.TRUE;
toolbeltConfiguration.put("retain.mantle." + className, 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().getNoiseCacheSize();
int cacheSize = IrisSettings.get().getPerformance().getCacheSize();
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,13 +86,11 @@ 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;
@@ -43,7 +43,6 @@ import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
import com.volmit.iris.util.scheduling.jobs.QueueJob;
import io.papermc.lib.PaperLib;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bukkit.Chunk;
@@ -61,10 +60,7 @@ 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.atomic.AtomicBoolean;
import java.util.concurrent.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -190,32 +186,54 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
int r = 1;
int cX = i.getLocation().getBlockX() >> 4;
int cZ = i.getLocation().getBlockZ() >> 4;
for (int x = -r; x <= r; x++) {
for (int z = -r; z <= r; z++) {
mantle.getChunk(i.getLocation().getChunk()).flag(MantleFlag.DISCOVERED, true);
mantle.getChunk(cX + x, cZ + z).flag(MantleFlag.DISCOVERED, true);
}
}
}
}
private void updateChunks() {
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
int r = 1;
int radius = 1;
int diameter = radius * 2 + 1;
int count = (diameter * diameter) + 1;
Chunk c = i.getLocation().getChunk();
for (int x = -r; x <= r; x++) {
for (int z = -r; z <= r; z++) {
if (c.getWorld().isChunkLoaded(c.getX() + x, c.getZ() + z) && Chunks.isSafe(getEngine().getWorld().realWorld(), c.getX() + x, c.getZ() + z)) {
var players = new KList<>(getEngine().getWorld().realWorld().getPlayers());
var latch = new CountDownLatch(count * players.size());
if (IrisSettings.get().getWorld().isPostLoadBlockUpdates()) {
getEngine().updateChunk(c.getWorld().getChunkAt(c.getX() + x, c.getZ() + z));
for (Player i : players) {
if (!i.isOnline() || !i.isValid() || i.isDead()) {
for (int j = 0; j < count; j++) {
latch.countDown();
}
continue;
}
Iris.platform.getEntityScheduler(i).run(() -> {
Chunk c = i.getLocation().getChunk();
for (int x = -radius; x <= radius; x++) {
for (int z = -radius; z <= radius; z++) {
int cX = c.getX() + x;
int cZ = c.getZ() + z;
if (!c.getWorld().isChunkLoaded(cX, cZ) || !Chunks.isSafe(getEngine().getWorld().realWorld(), cX, cZ)) {
latch.countDown();
continue;
}
if (IrisSettings.get().getWorld().isPostLoadBlockUpdates()) {
Chunk cx = c.getWorld().getChunkAt(cX, cZ);
J.a(() -> {
getEngine().updateChunk(cx);
latch.countDown();
});
} else latch.countDown();
if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
Chunk cx = getEngine().getWorld().realWorld().getChunkAt(c.getX() + x, c.getZ() + z);
int finalX = c.getX() + x;
int finalZ = c.getZ() + z;
J.a(() -> getMantle().raiseFlag(finalX, finalZ, MantleFlag.INITIAL_SPAWNED_MARKER,
Chunk cx = getEngine().getWorld().realWorld().getChunkAt(cX, cZ);
J.a(() -> getMantle().raiseFlag(cX, cZ, MantleFlag.INITIAL_SPAWNED_MARKER,
() -> {
J.a(() -> spawnIn(cx, true), RNG.r.i(5, 200));
getSpawnersFromMarkers(cx).forEach((blockf, spawners) -> {
@@ -231,8 +249,17 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
}
}
}
latch.countDown();
}, () -> {
for (int j = 0; j < count; j++) {
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException ignored) {}
}
private boolean onAsyncTick() {
@@ -440,7 +467,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
//INMS.get().injectBiomesFromMantle(e, getMantle());
if (!IrisSettings.get().getGenerator().earlyCustomBlocks) return;
Iris.tickets.addTicket(e);
e.addPluginChunkTicket(Iris.instance);
J.s(() -> {
var chunk = getMantle().getChunk(e).use();
int minY = getTarget().getWorld().minHeight();
@@ -452,7 +479,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
});
} finally {
chunk.release();
Iris.tickets.removeTicket(e);
e.removePluginChunkTicket(Iris.instance);
}
}, RNG.r.i(20, 60));
}
@@ -493,8 +520,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
e.setCancelled(true);
warmupAreaAsync(e.getPlayer(), e.getTo(), () -> J.s(() -> {
ignoreTP.set(true);
e.getPlayer().teleport(e.getTo(), e.getCause());
ignoreTP.set(false);
Iris.platform.teleportAsync(e.getPlayer(), e.getTo(), e.getCause())
.thenRun(() -> ignoreTP.set(false));
}));
}
}
@@ -514,7 +541,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
futures.add(MultiBurst.burst.completeValue(()
-> PaperLib.getChunkAtAsync(to.getWorld(),
-> Iris.platform.getChunkAtAsync(to.getWorld(),
(to.getBlockX() >> 4) + finalI,
(to.getBlockZ() >> 4) + finalJ,
true, IrisSettings.get().getWorld().getAsyncTeleport().isUrgent()).get()));
@@ -551,19 +578,17 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
IrisPosition pos = new IrisPosition((c.getX() << 4) + x, y, (c.getZ() << 4) + z);
if (mark.isEmptyAbove()) {
AtomicBoolean remove = new AtomicBoolean(false);
Boolean remove = Iris.platform.getRegionScheduler()
.run(c.getWorld(), c.getX(), c.getZ(), () -> c.getBlock(x, y + 1, z).getType().isSolid() || c.getBlock(x, y + 2, z).getType().isSolid())
.getResult()
.exceptionally(e -> {
Iris.reportError(e);
e.printStackTrace();
return false;
})
.join();
try {
J.sfut(() -> {
if (c.getBlock(x, y + 1, z).getBlockData().getMaterial().isSolid() || c.getBlock(x, y + 2, z).getBlockData().getMaterial().isSolid()) {
remove.set(true);
}
}).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if (remove.get()) {
if (remove == Boolean.TRUE) {
b.add(pos);
return;
}
@@ -18,7 +18,6 @@
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;
@@ -29,5 +28,5 @@ public interface BlockUpdater {
void updateChunk(Chunk c);
void update(int x, int y, int z, Chunk c, MantleChunk mc, RNG rf);
void update(int x, int y, int z, Chunk c, RNG rf);
}
@@ -48,7 +48,6 @@ 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;
@@ -65,7 +64,6 @@ import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.stream.ProceduralStream;
import io.papermc.lib.PaperLib;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
@@ -295,20 +293,20 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
var chunk = mantle.getChunk(c).use();
try {
Semaphore semaphore = new Semaphore(1024);
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(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);
if (!TileData.setTileState(block, v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey());
});
}, 0));
}, c, 1));
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, () -> {
chunk.iterate(Identifier.class, (x, y, z, v) -> {
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
});
}, 0));
}, c, 1));
chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, () -> {
PrecisionStopwatch p = PrecisionStopwatch.start();
@@ -341,48 +339,35 @@ 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, chunk, rng);
update(x, grid[x][z], z, c, rng);
}
}
chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) {
update(x, y, z, c, chunk, rng);
update(x, y, z, c, rng);
}
});
chunk.deleteSlices(MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds());
}, 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));
}
}, c, RNG.r.i(2, 20))); //Why is there a random delay here?
});
try {
semaphore.acquire(1024);
semaphore.acquire(3);
} catch (InterruptedException ignored) {}
} finally {
chunk.release();
}
}
private static Runnable run(Semaphore semaphore, Runnable runnable, int delay) {
private static Runnable run(Semaphore semaphore, Runnable runnable, Chunk chunk, int delay) {
return () -> {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!semaphore.tryAcquire())
return;
J.s(() -> {
Iris.platform.getRegionScheduler().runDelayed(chunk.getWorld(), chunk.getX(), chunk.getZ(), () -> {
try {
runnable.run();
} finally {
@@ -395,7 +380,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@BlockCoordinates
@Override
default void update(int x, int y, int z, Chunk c, MantleChunk mc, RNG rf) {
default void update(int x, int y, int z, Chunk c, RNG rf) {
Block block = c.getBlock(x, y, z);
BlockData data = block.getBlockData();
blockUpdatedMetric();
@@ -408,11 +393,17 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
if (slot != null) {
KList<IrisLootTable> tables = getLootTables(rx, block, mc);
KList<IrisLootTable> tables = getLootTables(rx, block);
try {
Bukkit.getPluginManager().callEvent(new IrisLootEvent(this, block, slot, tables));
if (tables.isEmpty()) return;
if (!tables.isEmpty()){
Iris.debug("IrisLootEvent has been accessed");
}
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState();
addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15);
@@ -478,23 +469,13 @@ 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, mc);
PlacedObject po = getObjectPlacement(rx, ry, rz);
if (po != null && po.getPlacement() != null) {
if (B.isStorageChest(b.getBlockData())) {
IrisLootTable table = po.getPlacement().getTable(b.getBlockData(), getData());
@@ -545,8 +526,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
if (IrisLootEvent.callLootEvent(items, inv, world, x, y, z))
return;
if (PaperLib.isPaper() && getWorld().hasRealWorld()) {
PaperLib.getChunkAtAsync(getWorld().realWorld(), x >> 4, z >> 4).thenAccept((c) -> {
if (world != null) {
final int cX = x >> 4, cZ = z >> 4;
Iris.platform.getChunkAtAsync(world, cX, cZ, true, false).thenAccept((c) -> {
Runnable r = () -> {
for (ItemStack i : items) {
inv.addItem(i);
@@ -555,10 +537,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
scramble(inv, rng);
};
if (Bukkit.isPrimaryThread()) {
if (Iris.platform.isOwnedByCurrentRegion(world, cX, cZ)) {
r.run();
} else {
J.s(r);
Iris.platform.getRegionScheduler().run(world, cX, cZ, r);
}
});
} else {
@@ -817,16 +799,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
default PlacedObject getObjectPlacement(int x, int y, int z) {
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);
String objectAt = getMantle().getMantle().get(x, y, z, String.class);
if (objectAt == null || objectAt.isEmpty()) {
return null;
}
@@ -836,7 +809,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
int id = Integer.parseInt(v[1]);
JigsawPieceContainer container = chunk.get(x & 15, y, z & 15, JigsawPieceContainer.class);
JigsawPieceContainer container = getMantle().getMantle().get(x, y, z, JigsawPieceContainer.class);
if (container != null) {
IrisJigsawPiece piece = container.load(getData());
if (piece.getObject().equals(object))
@@ -912,7 +885,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
player.sendMessage(C.GOLD + "No strongholds in world.");
} else {
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
J.s(() -> player.teleport(ll));
Iris.platform.teleportAsync(player, ll);
}
return;
@@ -20,39 +20,34 @@ package com.volmit.iris.engine.framework;
import com.volmit.iris.Iris;
import com.volmit.iris.core.events.IrisEngineHotloadEvent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.plugin.VolmitSender;
import de.crazydev22.platformutils.scheduler.task.Task;
import org.bukkit.*;
import org.bukkit.entity.EnderSignal;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
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.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.inventory.EquipmentSlot;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class EngineAssignedWorldManager extends EngineAssignedComponent implements EngineWorldManager, Listener {
private final int taskId;
private final Task task;
protected AtomicBoolean ignoreTP = new AtomicBoolean(false);
public EngineAssignedWorldManager() {
super(null, null);
taskId = -1;
task = null;
}
public EngineAssignedWorldManager(Engine engine) {
super(engine, "World");
Iris.instance.registerListener(this);
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::onTick, 0, 0);
task = Iris.platform.getGlobalScheduler().runAtFixedRate(this::onTick, 1, 1);
}
@EventHandler
@@ -129,6 +124,6 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
public void close() {
super.close();
Iris.instance.unregisterListener(this);
Bukkit.getScheduler().cancelTask(taskId);
if (task != null) task.cancel();
}
}
@@ -18,6 +18,7 @@
package com.volmit.iris.engine.framework;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
@@ -36,7 +37,6 @@ import com.volmit.iris.util.matter.MatterCavern;
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.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.scheduling.jobs.SingleJob;
import org.bukkit.Location;
@@ -114,7 +114,7 @@ public interface Locator<T> {
default void find(Player player, boolean teleport, String message) {
find(player, location -> {
if (teleport) {
J.s(() -> player.teleport(location));
Iris.platform.teleportAsync(player, location);
} else {
player.sendMessage(C.GREEN + message + " at: " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ());
}
@@ -24,6 +24,7 @@ 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 {
@@ -82,16 +83,6 @@ 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;
@@ -0,0 +1,111 @@
package com.volmit.iris.engine.framework.placer;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IObjectPlacer;
import com.volmit.iris.engine.object.TileData;
import lombok.EqualsAndHashCode;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import static com.volmit.iris.Iris.platform;
@EqualsAndHashCode
public class SafeObjectPlacer implements IObjectPlacer {
private final World world;
private final IObjectPlacer placer;
public SafeObjectPlacer(World world, IObjectPlacer placer) {
this.world = world;
this.placer = placer;
}
@Override
public int getHighest(int x, int z, IrisData data) {
if (!platform.isOwnedByCurrentRegion(world, x >> 4, z >> 4)) {
return platform.getRegionScheduler().run(world, x >> 4, z >> 4, () -> getHighest(x, z, data))
.getResult()
.join();
}
return placer.getHighest(x, z, data);
}
@Override
public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
if (!platform.isOwnedByCurrentRegion(world, x >> 4, z >> 4)) {
if (platform.isTickThread()) throw new IllegalStateException("Cannot run async on primary thread!");
return platform.getRegionScheduler().run(world, x >> 4, z >> 4, () -> getHighest(x, z, data, ignoreFluid))
.getResult()
.join();
}
return placer.getHighest(x, z, data);
}
@Override
public void set(int x, int y, int z, BlockData d) {
if (!platform.isOwnedByCurrentRegion(world, x >> 4, z >> 4)) {
if (platform.isTickThread()) throw new IllegalStateException("Cannot run async on primary thread!");
platform.getRegionScheduler().run(world, x >> 4, z >> 4, () -> set(x, y, z, d)).getResult().join();
} else placer.set(x, y, z, d);
}
@Override
public BlockData get(int x, int y, int z) {
if (!platform.isOwnedByCurrentRegion(world, x >> 4, z >> 4)) {
if (platform.isTickThread()) throw new IllegalStateException("Cannot run async on primary thread!");
return platform.getRegionScheduler().run(world, x >> 4, z >> 4, () -> get(x, y, z)).getResult().join();
}
return placer.get(x, y, z);
}
@Override
public boolean isPreventingDecay() {
return placer.isPreventingDecay();
}
@Override
public boolean isCarved(int x, int y, int z) {
return placer.isCarved(x, y, z);
}
@Override
public boolean isSolid(int x, int y, int z) {
if (!platform.isOwnedByCurrentRegion(world, x >> 4, z >> 4)) {
if (platform.isTickThread()) throw new IllegalStateException("Cannot run async on primary thread!");
return platform.getRegionScheduler().run(world, x >> 4, z >> 4, () -> isSolid(x, y, z)).getResult().join();
}
return placer.isSolid(x, y, z);
}
@Override
public boolean isUnderwater(int x, int z) {
if (!platform.isOwnedByCurrentRegion(world, x >> 4, z >> 4)) {
if (platform.isTickThread()) throw new IllegalStateException("Cannot run async on primary thread!");
return platform.getRegionScheduler().run(world, x >> 4, z >> 4, () -> isUnderwater(x, z)).getResult().join();
}
return placer.isUnderwater(x, z);
}
@Override
public int getFluidHeight() {
return placer.getFluidHeight();
}
@Override
public boolean isDebugSmartBore() {
return placer.isDebugSmartBore();
}
@Override
public void setTile(int xx, int yy, int zz, TileData tile) {
if (!platform.isOwnedByCurrentRegion(world, xx >> 4, zz >> 4)) {
if (platform.isTickThread()) throw new IllegalStateException("Cannot run async on primary thread!");
platform.getRegionScheduler().run(world, xx >> 4, zz >> 4, () -> setTile(xx, yy, zz, tile)).getResult().join();
} else placer.setTile(xx, yy, zz, tile);
}
@Override
public Engine getEngine() {
return placer.getEngine();
}
}
@@ -59,11 +59,6 @@ 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);
@@ -83,6 +78,12 @@ 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
@@ -124,13 +125,4 @@ 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;
}
}
@@ -154,14 +154,15 @@ 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) -> {
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);
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);
}, null, getData().getEngine() != null ? getData() : eng.getData()) != -1;
}
//TODO properly fix for folia
public void place(WorldObjectPlacer placer, Consumer<Boolean> consumer) {
J.s(() -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine())));
Iris.platform.getRegionScheduler().run(placer.getWorld(), position.getX() >> 4, position.getZ() >> 4, () -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine())));
}
private void generateOutwards() {
@@ -29,6 +29,8 @@ 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;
@@ -36,6 +38,7 @@ 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;
@@ -46,7 +49,10 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public interface EngineMantle extends MatterGenerator {
import static com.volmit.iris.util.parallel.StreamUtils.forEach;
import static com.volmit.iris.util.parallel.StreamUtils.streamRadius;
public interface EngineMantle {
BlockData AIR = B.get("AIR");
Mantle getMantle();
@@ -174,6 +180,49 @@ public interface EngineMantle extends MatterGenerator {
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()) {
@@ -213,6 +262,9 @@ public interface EngineMantle extends MatterGenerator {
default int getLoadedRegionCount() {
return getMantle().getLoadedRegionCount();
}
default long getLastUseMapMemoryUsage(){
return getMantle().LastUseMapMemoryUsage();
}
MantleJigsawComponent getJigsawComponent();
@@ -238,7 +290,7 @@ public interface EngineMantle extends MatterGenerator {
if (!isCovered(x, z)) return;
MantleChunk chunk = getMantle().getChunk(x, z).use();
try {
chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> {
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
chunk.deleteSlices(BlockData.class);
chunk.deleteSlices(String.class);
chunk.deleteSlices(MatterCavern.class);
@@ -249,7 +301,7 @@ public interface EngineMantle extends MatterGenerator {
}
}
default int getUnloadRegionCount() {
default long getUnloadRegionCount() {
return getMantle().getUnloadRegionCount();
}
@@ -44,7 +44,9 @@ import lombok.Data;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
@@ -52,21 +54,20 @@ 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 Map<Long, MantleChunk> cachedChunks;
private final KMap<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, boolean multicore) {
public MantleWriter(EngineMantle engineMantle, Mantle mantle, int x, int z, int radius) {
this.engineMantle = engineMantle;
this.mantle = mantle;
this.radius = radius * 2;
int d = this.radius + 1;
this.cachedChunks = multicore ? new KMap<>(d * d, 0.75f, Math.max(32, Runtime.getRuntime().availableProcessors() * 4)) : new HashMap<>(d * d);
this.cachedChunks = new KMap<>();
this.radius = radius;
this.x = x;
this.z = z;
int r = radius / 2;
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());
@@ -181,13 +182,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
return null;
}
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();
}
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
if (chunk == null) Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
return chunk;
}
@@ -35,6 +35,7 @@ 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;
@@ -44,7 +45,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, ReservedFlag.JIGSAW, 1);
super(engineMantle, ReservedFlag.JIGSAW, 2);
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().keys()) {
for (BlockVector j : clump.getBlocks().keySet()) {
int nx = j.getBlockX() + x;
int ny = j.getBlockY() + y;
int nz = j.getBlockZ() + z;
@@ -18,10 +18,13 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.placer.SafeObjectPlacer;
import de.crazydev22.platformutils.Type;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.Nullable;
public interface IObjectPlacer {
int getHighest(int x, int z, IrisData data);
@@ -46,9 +49,11 @@ 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();
default IObjectPlacer sync(World world) {
if (Iris.platform.getType() == Type.FOLIA)
return new SafeObjectPlacer(world, this);
return this;
}
}
@@ -67,6 +67,7 @@ public class IrisCommand {
return;
}
var scheduler = Iris.platform.getGlobalScheduler();
for (String command : commands) {
command = (command.startsWith("/") ? command.replaceFirst("/", "") : command)
.replaceAll("\\Q{x}\\E", String.valueOf(at.getBlockX()))
@@ -74,9 +75,9 @@ public class IrisCommand {
.replaceAll("\\Q{z}\\E", String.valueOf(at.getBlockZ()));
final String finalCommand = command;
if (repeat) {
Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), delay, repeatDelay);
scheduler.runAtFixedRate(() -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), Math.max(delay, 1), Math.max(repeatDelay, 1));
} else {
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), delay);
scheduler.runDelayed(() -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), Math.max(delay, 1));
}
}
}
@@ -122,10 +122,6 @@ 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"));
@@ -164,7 +160,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("IRON_CHAIN", "IRON_BARS"));
filters.add(new IrisCompatabilityBlockFilter("CHAIN", "IRON_BARS"));
filters.add(new IrisCompatabilityBlockFilter("NETHERITE_BLOCK", "QUARTZ_BLOCK"));
filters.add(new IrisCompatabilityBlockFilter("BLACKSTONE", "COBBLESTONE"));
filters.add(new IrisCompatabilityBlockFilter("BASALT", "STONE"));
@@ -258,6 +254,7 @@ 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,18 +249,14 @@ 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\nFile extension: .engine.kts")
@Desc("A list of scripts executed on engine setup")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> engineScripts = new KList<>();
@Desc("A list of scripts executed on data setup\nFile extension: .data.kts")
@Desc("A list of scripts executed on data setup")
@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\nFile extension: .engine.kts")
@Desc("The script to create the dimension mode instead of using provided types")
private String script;
public EngineMode create(Engine engine) {
@@ -24,7 +24,6 @@ import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -194,7 +193,7 @@ public class IrisEffect {
if (sound != null) {
Location part = p.getLocation().clone().add(RNG.r.i(-soundDistance, soundDistance), RNG.r.i(-soundDistance, soundDistance), RNG.r.i(-soundDistance, soundDistance));
J.s(() -> p.playSound(part, getSound(), (float) volume, (float) RNG.r.d(minPitch, maxPitch)));
schedule(p, () -> p.playSound(part, getSound(), (float) volume, (float) RNG.r.d(minPitch, maxPitch)));
}
if (particleEffect != null) {
@@ -204,7 +203,7 @@ public class IrisEffect {
part.add(RNG.r.d(), 0, RNG.r.d());
int offset = p.getWorld().getMinHeight();
if (extra != 0) {
J.s(() -> p.spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset),
schedule(p, () -> p.spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset),
part.getZ(),
particleCount,
randomAltX ? RNG.r.d(-particleAltX, particleAltX) : particleAltX,
@@ -212,7 +211,7 @@ public class IrisEffect {
randomAltZ ? RNG.r.d(-particleAltZ, particleAltZ) : particleAltZ,
extra));
} else {
J.s(() -> p.spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset), part.getZ(),
schedule(p, () -> p.spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset), part.getZ(),
particleCount,
randomAltX ? RNG.r.d(-particleAltX, particleAltX) : particleAltX,
randomAltY ? RNG.r.d(-particleAltY, particleAltY) : particleAltY,
@@ -231,10 +230,10 @@ public class IrisEffect {
return;
}
J.s(() -> p.removePotionEffect(getRealType()));
schedule(p, () -> p.removePotionEffect(getRealType()));
}
J.s(() -> p.addPotionEffect(new PotionEffect(getRealType(),
schedule(p, () -> p.addPotionEffect(new PotionEffect(getRealType(),
RNG.r.i(Math.min(potionTicksMax, potionTicksMin),
Math.max(potionTicksMax, potionTicksMin)),
getPotionStrength(),
@@ -254,7 +253,7 @@ public class IrisEffect {
if (sound != null) {
Location part = p.getLocation().clone().add(RNG.r.i(-soundDistance, soundDistance), RNG.r.i(-soundDistance, soundDistance), RNG.r.i(-soundDistance, soundDistance));
J.s(() -> p.getWorld().playSound(part, getSound(), (float) volume, (float) RNG.r.d(minPitch, maxPitch)));
schedule(p, () -> p.getWorld().playSound(part, getSound(), (float) volume, (float) RNG.r.d(minPitch, maxPitch)));
}
if (particleEffect != null) {
@@ -262,7 +261,7 @@ public class IrisEffect {
part.add(RNG.r.d(), 0, RNG.r.d());
int offset = p.getWorld().getMinHeight();
if (extra != 0) {
J.s(() -> p.getWorld().spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset),
schedule(p, () -> p.getWorld().spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset),
part.getZ(),
particleCount,
randomAltX ? RNG.r.d(-particleAltX, particleAltX) : particleAltX,
@@ -270,7 +269,7 @@ public class IrisEffect {
randomAltZ ? RNG.r.d(-particleAltZ, particleAltZ) : particleAltZ,
extra));
} else {
J.s(() -> p.getWorld().spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset), part.getZ(),
schedule(p, () -> p.getWorld().spawnParticle(particleEffect, part.getX(), part.getY() + offset + RNG.r.i(particleOffset), part.getZ(),
particleCount,
randomAltX ? RNG.r.d(-particleAltX, particleAltX) : particleAltX,
randomAltY ? RNG.r.d(-particleAltY, particleAltY) : particleAltY,
@@ -278,4 +277,8 @@ public class IrisEffect {
}
}
}
private void schedule(Entity entity, Runnable task) {
Iris.platform.getEntityScheduler(entity).run(task, null);
}
}
@@ -33,8 +33,6 @@ import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.plugin.Chunks;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -50,13 +48,11 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.Lootable;
import org.bukkit.util.Vector;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static com.volmit.iris.util.data.registry.Particles.ITEM;
@@ -166,12 +162,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 location to find the target location. You can spawn any entity this way.\nFile extension: .spawn.kts")
@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.")
@RegistryListResource(IrisScript.class)
private String spawnerScript = "";
@ArrayType(min = 1, type = String.class)
@Desc("Executed post spawn you can modify the entity however you want with it\nFile extension: .postspawn.kts")
@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.")
@RegistryListResource(IrisScript.class)
private KList<String> postSpawnScripts = new KList<>();
@@ -183,38 +179,34 @@ public class IrisEntity extends IrisRegistrant {
return spawn(gen, at, new RNG(at.hashCode()));
}
public Entity spawn(Engine gen, Location at, RNG rng) {
public Entity spawn(Engine gen, final Location at, RNG rng) {
if (!Iris.platform.isOwnedByCurrentRegion(at)) {
try {
final Location finalAt = at;
return Iris.platform.getRegionScheduler().run(at, () -> spawn(gen, finalAt, rng))
.getResult()
.get(500, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
return null;
}
}
if (!Chunks.isSafe(at)) {
return null;
}
if (isSpawnEffectRiseOutOfGround()) {
AtomicReference<Location> f = new AtomicReference<>(at);
try {
J.sfut(() -> {
if (Chunks.hasPlayersNearby(f.get())) {
Location b = f.get().clone();
Location start = new Location(b.getWorld(), b.getX(), b.getY() - 5, b.getZ());
f.set(start);
}
}).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
at = f.get();
if (isSpawnEffectRiseOutOfGround() && Chunks.hasPlayersNearby(at)) {
at.add(0, -5, 0);
}
Entity ee = doSpawn(at);
if (ee == null && !Chunks.isSafe(at)) {
if (ee == null) {
return null;
}
if (!spawnerScript.isEmpty() && ee == null) {
synchronized (this) {
try {
ee = (Entity) gen.getExecution().spawnMob(spawnerScript, at);
ee = (Entity) gen.getExecution().spawnMob(spawnerScript, at.clone());
} catch (Throwable ex) {
Iris.error("You must return an Entity in your scripts to use entity scripts!");
ex.printStackTrace();
@@ -241,111 +233,96 @@ public class IrisEntity extends IrisRegistrant {
int gg = 0;
for (IrisEntity i : passengers) {
Entity passenger = i.spawn(gen, at, rng.nextParallelRNG(234858 + gg++));
if (!Bukkit.isPrimaryThread()) {
J.s(() -> e.addPassenger(passenger));
}
e.addPassenger(i.spawn(gen, at, rng.nextParallelRNG(234858 + gg++)));
}
if (e instanceof Attributable) {
Attributable a = (Attributable) e;
if (e instanceof Attributable attributable) {
for (IrisAttributeModifier i : getAttributes()) {
i.apply(rng, a);
i.apply(rng, attributable);
}
}
if (e instanceof Lootable) {
Lootable l = (Lootable) e;
if (e instanceof Lootable lootable && getLoot().getTables().isNotEmpty()) {
lootable.setLootTable(new LootTable() {
@Override
public NamespacedKey getKey() {
return new NamespacedKey(Iris.instance, "loot-" + IrisEntity.this.hashCode());
}
if (getLoot().getTables().isNotEmpty()) {
Location finalAt = at;
l.setLootTable(new LootTable() {
@Override
public NamespacedKey getKey() {
return new NamespacedKey(Iris.instance, "loot-" + IrisEntity.this.hashCode());
@Override
public Collection<ItemStack> populateLoot(Random random, LootContext context) {
KList<ItemStack> items = new KList<>();
for (String fi : getLoot().getTables()) {
IrisLootTable i = gen.getData().getLootLoader().load(fi);
items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, at.getWorld(), at.getBlockX(), at.getBlockY(), at.getBlockZ()));
}
@Override
public Collection<ItemStack> populateLoot(Random random, LootContext context) {
KList<ItemStack> items = new KList<>();
return items;
}
for (String fi : getLoot().getTables()) {
IrisLootTable i = gen.getData().getLootLoader().load(fi);
items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getWorld(), finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ()));
}
return items;
@Override
public void fillInventory(Inventory inventory, Random random, LootContext context) {
for (ItemStack i : populateLoot(random, context)) {
inventory.addItem(i);
}
@Override
public void fillInventory(Inventory inventory, Random random, LootContext context) {
for (ItemStack i : populateLoot(random, context)) {
inventory.addItem(i);
}
gen.scramble(inventory, rng);
}
});
}
gen.scramble(inventory, rng);
}
});
}
if (e instanceof LivingEntity) {
LivingEntity l = (LivingEntity) e;
l.setAI(isAi());
l.setCanPickupItems(isPickupItems());
if (e instanceof LivingEntity living) {
living.setAI(isAi());
living.setCanPickupItems(isPickupItems());
if (getLeashHolder() != null) {
l.setLeashHolder(getLeashHolder().spawn(gen, at, rng.nextParallelRNG(234548)));
living.setLeashHolder(getLeashHolder().spawn(gen, at, rng.nextParallelRNG(234548)));
}
l.setRemoveWhenFarAway(isRemovable());
living.setRemoveWhenFarAway(isRemovable());
if (getHelmet() != null && rng.i(1, getHelmet().getRarity()) == 1) {
l.getEquipment().setHelmet(getHelmet().get(gen.isStudio(), rng));
living.getEquipment().setHelmet(getHelmet().get(gen.isStudio(), rng));
}
if (getChestplate() != null && rng.i(1, getChestplate().getRarity()) == 1) {
l.getEquipment().setChestplate(getChestplate().get(gen.isStudio(), rng));
living.getEquipment().setChestplate(getChestplate().get(gen.isStudio(), rng));
}
if (getLeggings() != null && rng.i(1, getLeggings().getRarity()) == 1) {
l.getEquipment().setLeggings(getLeggings().get(gen.isStudio(), rng));
living.getEquipment().setLeggings(getLeggings().get(gen.isStudio(), rng));
}
if (getBoots() != null && rng.i(1, getBoots().getRarity()) == 1) {
l.getEquipment().setBoots(getBoots().get(gen.isStudio(), rng));
living.getEquipment().setBoots(getBoots().get(gen.isStudio(), rng));
}
if (getMainHand() != null && rng.i(1, getMainHand().getRarity()) == 1) {
l.getEquipment().setItemInMainHand(getMainHand().get(gen.isStudio(), rng));
living.getEquipment().setItemInMainHand(getMainHand().get(gen.isStudio(), rng));
}
if (getOffHand() != null && rng.i(1, getOffHand().getRarity()) == 1) {
l.getEquipment().setItemInOffHand(getOffHand().get(gen.isStudio(), rng));
living.getEquipment().setItemInOffHand(getOffHand().get(gen.isStudio(), rng));
}
}
if (e instanceof Ageable && isBaby()) {
((Ageable) e).setBaby();
if (e instanceof Ageable ageable && isBaby()) {
ageable.setBaby();
}
if (e instanceof Panda) {
((Panda) e).setMainGene(getPandaMainGene());
((Panda) e).setMainGene(getPandaHiddenGene());
if (e instanceof Panda panda) {
panda.setMainGene(getPandaMainGene());
panda.setMainGene(getPandaHiddenGene());
}
if (e instanceof Villager) {
Villager villager = (Villager) e;
if (e instanceof Villager villager) {
villager.setRemoveWhenFarAway(false);
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
villager.setPersistent(true);
}, 1);
villager.setPersistent(true);
}
if (e instanceof Mob) {
Mob m = (Mob) e;
m.setAware(isAware());
if (e instanceof Mob mob) {
mob.setAware(isAware());
}
if (spawnEffect != null) {
@@ -365,41 +342,35 @@ public class IrisEntity extends IrisRegistrant {
rawCommands.forEach(r -> r.run(fat));
}
Location finalAt1 = at;
if (isSpawnEffectRiseOutOfGround() && e instanceof LivingEntity living && Chunks.hasPlayersNearby(at)) {
e.setInvulnerable(true);
living.setAI(false);
living.setCollidable(false);
living.setNoDamageTicks(100000);
AtomicInteger t = new AtomicInteger(0);
Iris.platform.getRegionScheduler().runAtFixedRate(at, task -> {
if (t.get() > 100) {
task.cancel();
return;
}
J.s(() -> {
if (isSpawnEffectRiseOutOfGround() && e instanceof LivingEntity && Chunks.hasPlayersNearby(finalAt1)) {
Location start = finalAt1.clone();
e.setInvulnerable(true);
((LivingEntity) e).setAI(false);
((LivingEntity) e).setCollidable(false);
((LivingEntity) e).setNoDamageTicks(100000);
AtomicInteger t = new AtomicInteger(0);
AtomicInteger v = new AtomicInteger(0);
v.set(J.sr(() -> {
if (t.get() > 100) {
J.csr(v.get());
return;
t.incrementAndGet();
if (e.getLocation().getBlock().getType().isSolid() || living.getEyeLocation().getBlock().getType().isSolid()) {
Iris.platform.teleportAsync(e, at.add(0, 0.1, 0));
Material material = living.getEyeLocation().subtract(0, 2, 0).getBlock().getType();
if (!material.isAir()) e.getWorld().spawnParticle(ITEM, living.getEyeLocation(), 6, 0.2, 0.4, 0.2, 0.06f, new ItemStack(material));
if (M.r(0.2)) {
e.getWorld().playSound(e.getLocation(), Sound.BLOCK_CHORUS_FLOWER_GROW, 0.8f, 0.1f);
}
t.incrementAndGet();
if (e.getLocation().getBlock().getType().isSolid() || ((LivingEntity) e).getEyeLocation().getBlock().getType().isSolid()) {
e.teleport(start.add(new Vector(0, 0.1, 0)));
ItemStack itemCrackData = new ItemStack(((LivingEntity) e).getEyeLocation().clone().subtract(0, 2, 0).getBlock().getBlockData().getMaterial());
e.getWorld().spawnParticle(ITEM, ((LivingEntity) e).getEyeLocation(), 6, 0.2, 0.4, 0.2, 0.06f, itemCrackData);
if (M.r(0.2)) {
e.getWorld().playSound(e.getLocation(), Sound.BLOCK_CHORUS_FLOWER_GROW, 0.8f, 0.1f);
}
} else {
J.csr(v.get());
((LivingEntity) e).setNoDamageTicks(0);
((LivingEntity) e).setCollidable(true);
((LivingEntity) e).setAI(true);
e.setInvulnerable(false);
}
}, 0));
}
});
} else {
task.cancel();
living.setNoDamageTicks(0);
living.setCollidable(true);
living.setAI(true);
e.setInvulnerable(false);
}
}, 1, 1);
}
return e;
@@ -429,29 +400,6 @@ public class IrisEntity extends IrisRegistrant {
return null;
}
if (!Bukkit.isPrimaryThread()) {
// Someone called spawn (worldedit maybe?) on a non server thread
// Due to the structure of iris, we will call it sync and busy wait until it's done.
AtomicReference<Entity> ae = new AtomicReference<>();
try {
J.s(() -> ae.set(doSpawn(at)));
} catch (Throwable e) {
return null;
}
PrecisionStopwatch p = PrecisionStopwatch.start();
while (ae.get() == null) {
J.sleep(25);
if (p.getMilliseconds() > 500) {
return null;
}
}
return ae.get();
}
if (isSpecialType()) {
return Iris.service(ExternalDataSVC.class).spawnMob(at, Identifier.fromString(specialType));
}
@@ -36,8 +36,14 @@ import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.volmit.iris.Iris.platform;
@Snippet("entity-spawn")
@Accessors(chain = true)
@NoArgsConstructor
@@ -164,8 +170,15 @@ public class IrisEntitySpawn implements IRare {
return null;
}
if (!ignoreSurfaces && !irisEntity.getSurface().matches(at.clone().subtract(0, 1, 0).getBlock())) {
return null;
if (!ignoreSurfaces) {
Location block = at.clone().subtract(0, 1, 0);
BlockData data = platform.getRegionScheduler()
.run(block, () -> block.getBlock().getBlockData())
.getResult()
.join();
if (!irisEntity.getSurface().matches(data)) {
return null;
}
}
Vector3d boundingBox = INMS.get().getBoundingbox(irisEntity.getType());
@@ -199,15 +212,22 @@ public class IrisEntitySpawn implements IRare {
int startZ = center.getBlockZ() - (int) (boundingBox.z / 2);
int endZ = center.getBlockZ() + (int) (boundingBox.z / 2);
var region = platform.getRegionScheduler();
var lock = new Semaphore(Integer.MAX_VALUE, true);
var bool = new AtomicBoolean(true);
for (int x = startX; x <= endX; x++) {
for (int y = startY; y <= endY; y++) {
for (int z = startZ; z <= endZ; z++) {
if (world.getBlockAt(x, y, z).getType() != Material.AIR) {
return false;
}
Location l = new Location(world, x, y, z);
lock.acquireUninterruptibly();
region.run(l, () -> {
if (!bool.get()) return false;
return bool.compareAndSet(true, l.getBlock().getType() == Material.AIR);
}).getResult().exceptionally(f -> false).thenRun(lock::release);
}
}
}
return true;
lock.acquireUninterruptibly(Integer.MAX_VALUE);
return bool.get();
}
}
@@ -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.\nFile extension: .noise.kts")
@Desc("Instead of using the style property, use a custom noise generator to represent this style.")
@RegistryListResource(IrisScript.class)
private String script = null;
@MinNumber(0.00001)
@@ -30,15 +30,18 @@ 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.*;
import com.volmit.iris.util.math.AxisAlignedBB;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterMarker;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.IrisLock;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.scheduling.jobs.Job;
import com.volmit.iris.util.stream.ProceduralStream;
@@ -62,10 +65,7 @@ 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 Lock readLock;
protected transient final Lock writeLock;
protected transient final IrisLock readLock = new IrisLock("read-conclock");
@Getter
@Setter
protected transient volatile boolean smartBored = false;
@Getter
@Setter
protected transient IrisLock lock = new IrisLock("Preloadcache");
@Setter
protected transient AtomicCache<AxisAlignedBB> aabb = new AtomicCache<>();
@Getter
private VectorMap<BlockData> blocks;
@Getter
private VectorMap<TileData> states;
private KMap<BlockVector, BlockData> blocks;
private KMap<BlockVector, TileData> states;
@Getter
@Setter
private int w;
@@ -97,18 +97,15 @@ public class IrisObject extends IrisRegistrant {
private int h;
@Getter
@Setter
private transient Vector3i center;
private transient BlockVector center;
public IrisObject(int w, int h, int d) {
blocks = new VectorMap<>();
states = new VectorMap<>();
blocks = new KMap<>();
states = new KMap<>();
this.w = w;
this.h = h;
this.d = d;
center = new Vector3i(w / 2, h / 2, d / 2);
var lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
center = new BlockVector(w / 2, h / 2, d / 2);
}
public IrisObject() {
@@ -164,10 +161,10 @@ public class IrisObject extends IrisRegistrant {
PrecisionStopwatch p = PrecisionStopwatch.start();
BlockData vair = debug ? VAIR_DEBUG : VAIR;
writeLock.lock();
lock.lock();
AtomicInteger applied = new AtomicInteger();
if (blocks.isEmpty()) {
writeLock.unlock();
if (getBlocks().isEmpty()) {
lock.unlock();
Iris.warn("Cannot Smart Bore " + getLoadKey() + " because it has 0 blocks in it.");
smartBored = true;
return;
@@ -176,7 +173,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 : blocks.keys()) {
for (BlockVector i : getBlocks().keySet()) {
max.setX(Math.max(i.getX(), max.getX()));
min.setX(Math.min(i.getX(), min.getX()));
max.setY(Math.max(i.getY(), max.getY()));
@@ -196,7 +193,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE;
for (int ray = min.getBlockX(); ray <= max.getBlockX(); ray++) {
if (blocks.containsKey(new Vector3i(ray, finalRayY, rayZ))) {
if (getBlocks().containsKey(new BlockVector(ray, finalRayY, rayZ))) {
start = Math.min(ray, start);
end = Math.max(ray, end);
}
@@ -204,10 +201,10 @@ public class IrisObject extends IrisRegistrant {
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
for (int i = start; i <= end; i++) {
Vector3i v = new Vector3i(i, finalRayY, rayZ);
BlockVector v = new BlockVector(i, finalRayY, rayZ);
if (!vair.equals(blocks.get(v))) {
blocks.computeIfAbsent(v, (vv) -> vair);
if (!B.isAir(getBlocks().get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement();
}
}
@@ -225,7 +222,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE;
for (int ray = min.getBlockY(); ray <= max.getBlockY(); ray++) {
if (blocks.containsKey(new Vector3i(finalRayX, ray, rayZ))) {
if (getBlocks().containsKey(new BlockVector(finalRayX, ray, rayZ))) {
start = Math.min(ray, start);
end = Math.max(ray, end);
}
@@ -233,10 +230,10 @@ public class IrisObject extends IrisRegistrant {
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
for (int i = start; i <= end; i++) {
Vector3i v = new Vector3i(finalRayX, i, rayZ);
BlockVector v = new BlockVector(finalRayX, i, rayZ);
if (!vair.equals(blocks.get(v))) {
blocks.computeIfAbsent(v, (vv) -> vair);
if (!B.isAir(getBlocks().get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement();
}
}
@@ -254,7 +251,7 @@ public class IrisObject extends IrisRegistrant {
int end = Integer.MIN_VALUE;
for (int ray = min.getBlockZ(); ray <= max.getBlockZ(); ray++) {
if (blocks.containsKey(new Vector3i(finalRayX, rayY, ray))) {
if (getBlocks().containsKey(new BlockVector(finalRayX, rayY, ray))) {
start = Math.min(ray, start);
end = Math.max(ray, end);
}
@@ -262,10 +259,10 @@ public class IrisObject extends IrisRegistrant {
if (start != Integer.MAX_VALUE && end != Integer.MIN_VALUE) {
for (int i = start; i <= end; i++) {
Vector3i v = new Vector3i(finalRayX, rayY, i);
BlockVector v = new BlockVector(finalRayX, rayY, i);
if (!vair.equals(blocks.get(v))) {
blocks.computeIfAbsent(v, (vv) -> vair);
if (!B.isAir(getBlocks().get(v))) {
getBlocks().computeIfAbsent(v, (vv) -> vair);
applied.getAndIncrement();
}
}
@@ -276,7 +273,7 @@ public class IrisObject extends IrisRegistrant {
burst.complete();
smartBored = true;
writeLock.unlock();
lock.unlock();
Iris.debug("Smart Bore: " + getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied.get()) + ")");
}
@@ -287,8 +284,13 @@ public class IrisObject extends IrisRegistrant {
o.setLoadFile(getLoadFile());
o.setCenter(getCenter().clone());
blocks.forEach((i, v) -> o.blocks.put(i.clone(), v.clone()));
states.forEach((i, v) -> o.states.put(i.clone(), v.clone()));
for (BlockVector i : getBlocks().keySet()) {
o.getBlocks().put(i.clone(), Objects.requireNonNull(getBlocks().get(i)).clone());
}
for (BlockVector i : getStates().keySet()) {
o.getStates().put(i.clone(), Objects.requireNonNull(getStates().get(i)).clone());
}
return o;
}
@@ -298,18 +300,18 @@ public class IrisObject extends IrisRegistrant {
this.w = din.readInt();
this.h = din.readInt();
this.d = din.readInt();
center = new Vector3i(w / 2, h / 2, d / 2);
center = new BlockVector(w / 2, h / 2, d / 2);
int s = din.readInt();
for (int i = 0; i < s; i++) {
blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF()));
getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF()));
}
try {
int size = din.readInt();
for (int i = 0; i < size; i++) {
states.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
}
} catch (Throwable e) {
Iris.reportError(e);
@@ -325,7 +327,7 @@ public class IrisObject extends IrisRegistrant {
if (!din.readUTF().equals("Iris V2 IOB;")) {
return;
}
center = new Vector3i(w / 2, h / 2, d / 2);
center = new BlockVector(w / 2, h / 2, d / 2);
int s = din.readShort();
int i;
KList<String> palette = new KList<>();
@@ -337,13 +339,13 @@ public class IrisObject extends IrisRegistrant {
s = din.readInt();
for (i = 0; i < s; i++) {
blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort())));
getBlocks().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), B.get(palette.get(din.readShort())));
}
s = din.readInt();
for (i = 0; i < s; i++) {
states.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
getStates().put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), TileData.read(din));
}
}
@@ -355,7 +357,7 @@ public class IrisObject extends IrisRegistrant {
dos.writeUTF("Iris V2 IOB;");
KList<String> palette = new KList<>();
for (BlockData i : blocks.values()) {
for (BlockData i : getBlocks().values()) {
palette.addIfMissing(i.getAsString());
}
@@ -365,23 +367,21 @@ public class IrisObject extends IrisRegistrant {
dos.writeUTF(i);
}
dos.writeInt(blocks.size());
dos.writeInt(getBlocks().size());
for (var entry : blocks) {
var i = entry.getKey();
for (BlockVector i : getBlocks().keySet()) {
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
dos.writeShort(palette.indexOf(entry.getValue().getAsString()));
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString()));
}
dos.writeInt(states.size());
for (var entry : states) {
var i = entry.getKey();
dos.writeInt(getStates().size());
for (BlockVector i : getStates().keySet()) {
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
entry.getValue().toBinary(dos);
getStates().get(i).toBinary(dos);
}
}
@@ -389,7 +389,7 @@ public class IrisObject extends IrisRegistrant {
AtomicReference<IOException> ref = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
new Job() {
private int total = blocks.size() * 3 + states.size();
private int total = getBlocks().size() * 3 + getStates().size();
private int c = 0;
@Override
@@ -408,11 +408,11 @@ public class IrisObject extends IrisRegistrant {
KList<String> palette = new KList<>();
for (BlockData i : blocks.values()) {
for (BlockData i : getBlocks().values()) {
palette.addIfMissing(i.getAsString());
++c;
}
total -= blocks.size() - palette.size();
total -= getBlocks().size() - palette.size();
dos.writeShort(palette.size());
@@ -421,24 +421,22 @@ public class IrisObject extends IrisRegistrant {
++c;
}
dos.writeInt(blocks.size());
dos.writeInt(getBlocks().size());
for (var entry : blocks) {
var i = entry.getKey();
for (BlockVector i : getBlocks().keySet()) {
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
dos.writeShort(palette.indexOf(entry.getValue().getAsString()));
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString()));
++c;
}
dos.writeInt(states.size());
for (var entry : states) {
var i = entry.getKey();
dos.writeInt(getStates().size());
for (BlockVector i : getStates().keySet()) {
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
entry.getValue().toBinary(dos);
getStates().get(i).toBinary(dos);
++c;
}
} catch (IOException e) {
@@ -507,7 +505,7 @@ public class IrisObject extends IrisRegistrant {
BlockVector min = new BlockVector();
BlockVector max = new BlockVector();
for (BlockVector i : blocks.keys()) {
for (BlockVector i : getBlocks().keySet()) {
min.setX(Math.min(min.getX(), i.getX()));
min.setY(Math.min(min.getY(), i.getY()));
min.setZ(Math.min(min.getZ(), i.getZ()));
@@ -519,52 +517,62 @@ 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 Vector3i(w / 2, h / 2, d / 2);
center = new BlockVector(w / 2, h / 2, d / 2);
}
public void clean() {
VectorMap<BlockData> d = new VectorMap<>();
d.putAll(blocks);
KMap<BlockVector, BlockData> d = new KMap<>();
VectorMap<TileData> dx = new VectorMap<>();
dx.putAll(states);
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)));
}
blocks = d;
states = dx;
}
public Vector3i getSigned(int x, int y, int z) {
public BlockVector getSigned(int x, int y, int z) {
if (x >= w || y >= h || z >= d) {
throw new RuntimeException(x + " " + y + " " + z + " exceeds limit of " + w + " " + h + " " + d);
}
return (Vector3i) new Vector3i(x, y, z).subtract(center);
return new BlockVector(x, y, z).subtract(center).toBlockVector();
}
public void setUnsigned(int x, int y, int z, BlockData block) {
Vector3i v = getSigned(x, y, z);
BlockVector v = getSigned(x, y, z);
if (block == null) {
blocks.remove(v);
states.remove(v);
getBlocks().remove(v);
getStates().remove(v);
} else {
blocks.put(v, block);
getBlocks().put(v, block);
}
}
public void setUnsigned(int x, int y, int z, Block block, boolean legacy) {
Vector3i v = getSigned(x, y, z);
BlockVector v = getSigned(x, y, z);
if (block == null) {
blocks.remove(v);
states.remove(v);
getBlocks().remove(v);
getStates().remove(v);
} else {
BlockData data = block.getBlockData();
blocks.put(v, data);
getBlocks().put(v, data);
TileData state = TileData.getTileState(block, legacy);
if (state != null) {
Iris.debug("Saved State " + v);
states.put(v, state);
getStates().put(v, state);
}
}
}
@@ -861,9 +869,6 @@ 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());
@@ -872,12 +877,13 @@ public class IrisObject extends IrisRegistrant {
}
int max = j.getMaximumMarkers();
for (BlockVector i : list.shuffle()) {
for (BlockVector i : getBlocks().k().shuffle()) {
if (max <= 0) {
break;
}
BlockData data = blocks.get(i);
BlockData data = getBlocks().get(i);
for (BlockData k : j.getMark(rdata)) {
if (max <= 0) {
@@ -885,8 +891,8 @@ public class IrisObject extends IrisRegistrant {
}
if (j.isExact() ? k.matches(data) : k.getMaterial().equals(data.getMaterial())) {
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)));
boolean a = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 1, 0))));
boolean fff = !blocks.containsKey(new BlockVector(i.clone().add(new BlockVector(0, 2, 0))));
if (!marker.isEmptyAbove() || (a && fff)) {
markers.put(i, j.getMarker());
@@ -898,14 +904,13 @@ public class IrisObject extends IrisRegistrant {
}
}
for (var entry : blocks) {
var g = entry.getKey();
for (BlockVector g : getBlocks().keySet()) {
BlockData d;
TileData tile = null;
try {
d = entry.getValue();
tile = states.get(g);
d = getBlocks().get(g);
tile = getStates().get(g);
} catch (Throwable e) {
Iris.reportError(e);
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (cme)");
@@ -1023,12 +1028,12 @@ public class IrisObject extends IrisRegistrant {
if (stilting) {
readLock.lock();
IrisStiltSettings settings = config.getStiltSettings();
for (BlockVector g : blocks.keys()) {
for (BlockVector g : getBlocks().keySet()) {
BlockData d;
if (settings == null || settings.getPalette() == null) {
try {
d = blocks.get(g);
d = getBlocks().get(g);
} catch (Throwable e) {
Iris.reportError(e);
Iris.warn("Failed to read block node " + g.getBlockX() + "," + g.getBlockY() + "," + g.getBlockZ() + " in object " + getLoadKey() + " (stilt cme)");
@@ -1135,60 +1140,59 @@ public class IrisObject extends IrisRegistrant {
}
public void rotate(IrisObjectRotation r, int spinx, int spiny, int spinz) {
writeLock.lock();
VectorMap<BlockData> d = new VectorMap<>();
KMap<BlockVector, BlockData> d = new KMap<>();
for (var entry : blocks) {
d.put(r.rotate(entry.getKey(), spinx, spiny, spinz), r.rotate(entry.getValue(), spinx, spiny, spinz));
for (BlockVector i : getBlocks().keySet()) {
d.put(r.rotate(i.clone(), spinx, spiny, spinz), r.rotate(getBlocks().get(i).clone(),
spinx, spiny, spinz));
}
VectorMap<TileData> dx = new VectorMap<>();
KMap<BlockVector, TileData> dx = new KMap<>();
for (var entry : states) {
dx.put(r.rotate(entry.getKey(), spinx, spiny, spinz), entry.getValue());
for (BlockVector i : getStates().keySet()) {
dx.put(r.rotate(i.clone(), spinx, spiny, spinz), getStates().get(i));
}
blocks = d;
states = dx;
writeLock.unlock();
shrinkwrap();
}
public void place(Location at) {
readLock.lock();
for (var entry : blocks) {
var i = entry.getKey();
for (BlockVector i : getBlocks().keySet()) {
Block b = at.clone().add(0, getCenter().getY(), 0).add(i).getBlock();
b.setBlockData(Objects.requireNonNull(entry.getValue()), false);
b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false);
if (states.containsKey(i)) {
if (getStates().containsKey(i)) {
Iris.info(Objects.requireNonNull(states.get(i)).toString());
Objects.requireNonNull(states.get(i)).toBukkitTry(b);
Objects.requireNonNull(getStates().get(i)).toBukkitTry(b);
}
}
readLock.unlock();
}
public void placeCenterY(Location at) {
readLock.lock();
for (var entry : blocks) {
var i = entry.getKey();
for (BlockVector i : getBlocks().keySet()) {
Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock();
b.setBlockData(Objects.requireNonNull(entry.getValue()), false);
b.setBlockData(Objects.requireNonNull(getBlocks().get(i)), false);
if (states.containsKey(i)) {
Objects.requireNonNull(states.get(i)).toBukkitTry(b);
if (getStates().containsKey(i)) {
Objects.requireNonNull(getStates().get(i)).toBukkitTry(b);
}
}
readLock.unlock();
}
public synchronized KMap<BlockVector, BlockData> getBlocks() {
return blocks;
}
public synchronized KMap<BlockVector, TileData> getStates() {
return states;
}
public void unplaceCenterY(Location at) {
readLock.lock();
for (BlockVector i : blocks.keys()) {
for (BlockVector i : getBlocks().keySet()) {
at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i).getBlock().setBlockData(AIR, false);
}
readLock.unlock();
}
public IrisObject scaled(double scale, IrisObjectPlacementScaleInterpolator interpolation) {
@@ -1200,7 +1204,7 @@ public class IrisObject extends IrisRegistrant {
IrisPosition l1 = getAABB().max();
IrisPosition l2 = getAABB().min();
VectorMap<BlockData> placeBlock = new VectorMap<>();
@SuppressWarnings({"unchecked", "rawtypes"}) HashMap<BlockVector, BlockData> placeBlock = new HashMap();
Vector center = getCenter();
if (getH() == 2) {
@@ -1215,19 +1219,17 @@ 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)));
readLock.lock();
for (var entry : blocks) {
for (Map.Entry<BlockVector, BlockData> entry : blocks.entrySet()) {
BlockData bd = entry.getValue();
placeBlock.put(entry.getKey().clone().add(HALF).subtract(center)
.multiply(scale).add(sm1).toBlockVector(), bd);
}
readLock.unlock();
for (var entry : placeBlock) {
for (Map.Entry<BlockVector, BlockData> entry : placeBlock.entrySet()) {
BlockVector v = entry.getKey();
if (scale > 1) {
for (BlockVector vec : blocksBetweenTwoPoints(v.clone().add(center), v.clone().add(center).add(sm1))) {
oo.blocks.put(vec, entry.getValue());
oo.getBlocks().put(vec, entry.getValue());
}
} else {
oo.setUnsigned(v.getBlockX(), v.getBlockY(), v.getBlockZ(), entry.getValue());
@@ -1246,9 +1248,8 @@ public class IrisObject extends IrisRegistrant {
}
public void trilinear(int rad) {
writeLock.lock();
VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
KMap<BlockVector, BlockData> v = getBlocks().copy();
KMap<BlockVector, BlockData> b = new KMap<>();
BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv();
@@ -1273,13 +1274,11 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
writeLock.unlock();
}
public void tricubic(int rad) {
writeLock.lock();
VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
KMap<BlockVector, BlockData> v = getBlocks().copy();
KMap<BlockVector, BlockData> b = new KMap<>();
BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv();
@@ -1304,7 +1303,6 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
writeLock.unlock();
}
public void trihermite(int rad) {
@@ -1312,9 +1310,8 @@ public class IrisObject extends IrisRegistrant {
}
public void trihermite(int rad, double tension, double bias) {
writeLock.lock();
VectorMap<BlockData> v = blocks;
VectorMap<BlockData> b = new VectorMap<>();
KMap<BlockVector, BlockData> v = getBlocks().copy();
KMap<BlockVector, BlockData> b = new KMap<>();
BlockVector min = getAABB().minbv();
BlockVector max = getAABB().maxbv();
@@ -1339,13 +1336,11 @@ 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);
readLock.lock();
BlockData r = blocks.get(vv);
BlockData r = getBlocks().get(vv);
if (r != null && !r.getMaterial().isAir()) {
return r;
@@ -1353,7 +1348,7 @@ public class IrisObject extends IrisRegistrant {
double d = Double.MAX_VALUE;
for (var entry : blocks) {
for (Map.Entry<BlockVector, BlockData> entry : blocks.entrySet()) {
BlockData dat = entry.getValue();
if (dat.getMaterial().isAir()) {
@@ -1367,7 +1362,6 @@ public class IrisObject extends IrisRegistrant {
r = dat;
}
}
readLock.unlock();
return r;
}
@@ -18,7 +18,7 @@ public class IrisPreProcessors {
private String type = "dimension";
@Required
@Desc("The preprocessor scripts\nFile extension: .proc.kts")
@Desc("The preprocessor scripts")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> scripts = new KList<>();
@@ -20,7 +20,7 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.Desc;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
@Desc("The type of surface entities should spawn on")
@@ -47,8 +47,8 @@ public enum IrisSurface {
* @param state The blockstate
* @return True if it matches
*/
public boolean matches(Block state) {
Material type = state.getType();
public boolean matches(BlockData state) {
Material type = state.getMaterial();
if (type.isSolid()) {
return this == LAND || this == OVERWORLD || (this == ANIMAL
&& (type == Material.GRASS_BLOCK || type == Material.DIRT
@@ -1,10 +1,10 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.scheduling.J;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
@@ -80,7 +80,7 @@ public class LegacyTileData extends TileData {
@Override
public void toBukkit(Block block) {
J.s(() -> handler.toBukkit(block));
Iris.platform.getRegionScheduler().run(block.getLocation(), () -> handler.toBukkit(block));
}
@Override
@@ -38,10 +38,11 @@ import com.volmit.iris.util.data.IrisBiomeStorage;
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
import com.volmit.iris.util.io.ReactiveFolder;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
import io.papermc.lib.PaperLib;
import de.crazydev22.platformutils.scheduler.IRegionExecutor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
@@ -142,7 +143,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
@Override
public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) {
Location location = new Location(world, 0, 64, 0);
PaperLib.getChunkAtAsync(location)
Iris.platform.getChunkAtAsync(location)
.thenAccept(c -> {
World w = c.getWorld();
if (!w.getSpawnLocation().equals(location))
@@ -166,8 +167,6 @@ 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) {
@@ -197,17 +196,17 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
@Override
public void injectChunkReplacement(World world, int x, int z, Executor syncExecutor) {
public void injectChunkReplacement(World world, int x, int z, IRegionExecutor executor) {
try {
loadLock.acquire();
IrisBiomeStorage st = new IrisBiomeStorage();
TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
this.world.bind(world);
getEngine().generate(x << 4, z << 4, tc, IrisSettings.get().getGenerator().useMulticore);
generateNoise(world, RNG.r, x, z, tc);
Chunk c = PaperLib.getChunkAtAsync(world, x, z)
Chunk c = Iris.platform.getChunkAtAsync(world, x, z)
.thenApply(d -> {
Iris.tickets.addTicket(d);
if (d == null) throw new IllegalStateException("Chunk is null!");
d.addPluginChunkTicket(Iris.instance);
for (Entity ee : d.getEntities()) {
if (ee instanceof Player) {
@@ -217,6 +216,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
ee.remove();
}
engine.getWorldManager().onChunkLoad(d, false);
return d;
}).get();
@@ -224,7 +224,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
KList<CompletableFuture<?>> futures = new KList<>(1 + getEngine().getHeight() >> 4);
for (int i = getEngine().getHeight() >> 4; i >= 0; i--) {
int finalI = i << 4;
futures.add(CompletableFuture.runAsync(() -> {
futures.add(executor.queue(world, x, z, () -> {
for (int xx = 0; xx < 16; xx++) {
for (int yy = 0; yy < 16; yy++) {
for (int zz = 0; zz < 16; zz++) {
@@ -236,15 +236,15 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
}
}
}, syncExecutor));
}));
}
futures.add(CompletableFuture.runAsync(() -> INMS.get().placeStructures(c), syncExecutor));
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRunAsync(() -> {
Iris.tickets.removeTicket(c);
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
.thenCompose($ -> executor.queue(world, x, z, () -> INMS.get().placeStructures(c)))
.thenCompose($ -> executor.queue(world, x, z, () -> {
c.removePluginChunkTicket(Iris.instance);
engine.getWorldManager().onChunkLoad(c, true);
}, syncExecutor)
}))
.get();
Iris.debug("Regenerated " + x + " " + z);
@@ -255,14 +255,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
e.printStackTrace();
Iris.reportErrorChunk(x, z, e, "CHUNK");
Iris.error("======================================");
ChunkData d = Bukkit.createChunkData(world);
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData());
}
}
}
}
@@ -23,12 +23,12 @@ import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget;
import com.volmit.iris.engine.framework.Hotloadable;
import com.volmit.iris.util.data.DataProvider;
import de.crazydev22.platformutils.scheduler.IRegionExecutor;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
@Nullable
@@ -42,7 +42,7 @@ public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
@NotNull
EngineTarget getTarget();
void injectChunkReplacement(World world, int x, int z, Executor syncExecutor);
void injectChunkReplacement(World world, int x, int z, IRegionExecutor executor);
void close();
@@ -18,10 +18,11 @@
package com.volmit.iris.util.board;
import com.volmit.iris.Iris;
import de.crazydev22.platformutils.scheduler.task.Task;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
import java.util.Collections;
import java.util.Map;
@@ -34,7 +35,7 @@ public class BoardManager {
private final JavaPlugin plugin;
private final Map<UUID, Board> scoreboards;
private final BukkitTask updateTask;
private final Task updateTask;
private BoardSettings boardSettings;
@@ -42,7 +43,7 @@ public class BoardManager {
this.plugin = plugin;
this.boardSettings = boardSettings;
this.scoreboards = new ConcurrentHashMap<>();
this.updateTask = new BoardUpdateTask(this).runTaskTimer(plugin, 2L, 20L);
this.updateTask = Iris.platform.getGlobalScheduler().runAtFixedRate(new BoardUpdateTask(this), 2L, 20L);
plugin.getServer().getOnlinePlayers().forEach(this::setup);
}
@@ -20,7 +20,6 @@ package com.volmit.iris.util.board;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.UUID;
import java.util.function.Predicate;
@@ -30,7 +29,7 @@ import java.util.function.Predicate;
* @since 5/31/2018
*/
@RequiredArgsConstructor
public class BoardUpdateTask extends BukkitRunnable {
public class BoardUpdateTask implements Runnable {
private static final Predicate<UUID> PLAYER_IS_ONLINE = uuid -> Bukkit.getPlayer(uuid) != null;
@@ -2,55 +2,24 @@ package com.volmit.iris.util.cache;
import com.volmit.iris.util.function.Function2;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class ChunkCache2D<T> {
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 AtomicReferenceArray<T> cache;
private final Entry<T>[] cache;
@SuppressWarnings({"unchecked"})
public ChunkCache2D() {
this.cache = new Entry[256];
if (DYNAMIC) return;
for (int i = 0; i < cache.length; i++) {
cache[i] = FAST ? new FastEntry<>() : new Entry<>();
}
this.cache = new AtomicReferenceArray<>(256);
}
@SuppressWarnings({"unchecked"})
public T get(int x, int z, Function2<Integer, Integer, T> resolver) {
int key = ((z & 15) * 16) + (x & 15);
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);
}
T t = cache.get(key);
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;
}
if (t == null) {
t = resolver.apply(x, z);
cache.set(key, 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);
}
return t;
}
}
@@ -1,32 +1,24 @@
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 ConcurrentLinkedHashMap<Long, ChunkCache2D<T>> chunks;
private final KCache<Long, ChunkCache2D<T>> chunks;
private final Function2<Integer, Integer, T> resolver;
public WorldCache2D(Function2<Integer, Integer, T> resolver, int size) {
public WorldCache2D(Function2<Integer, Integer, T> resolver) {
this.resolver = resolver;
chunks = new ConcurrentLinkedHashMap.Builder<Long, ChunkCache2D<T>>()
.initialCapacity(size)
.maximumWeightedCapacity(size)
.concurrencyLevel(Math.max(32, Runtime.getRuntime().availableProcessors() * 4))
.build();
chunks = new KCache<>((x) -> new ChunkCache2D<>(), 1024);
}
public T get(int x, int z) {
ChunkCache2D<T> chunk = chunks.computeIfAbsent(Cache.key(x >> 4, z >> 4), $ -> new ChunkCache2D<>());
ChunkCache2D<T> chunk = chunks.get(Cache.key(x >> 4, z >> 4));
return chunk.get(x, z, resolver);
}
public long getSize() {
return chunks.size() * 256L;
}
public long getMaxSize() {
return chunks.capacity() * 256L;
return chunks.getSize() * 256L;
}
}
@@ -33,15 +33,7 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 7288942695300448163L;
public KMap() {
this(16);
}
public KMap(int initialCapacity) {
this(initialCapacity, 0.75f, 1);
}
public KMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
super();
}
public KMap(Map<K, V> gMap) {
@@ -0,0 +1,51 @@
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);
}
}
}
@@ -0,0 +1,60 @@
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;
}
}
@@ -316,17 +316,17 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
*
* @return array of Block objects representing the Cuboid corners
*/
public Block[] corners() {
Block[] res = new Block[8];
public Location[] corners() {
Location[] res = new Location[8];
World w = getWorld();
res[0] = w.getBlockAt(x1, y1, z1);
res[1] = w.getBlockAt(x1, y1, z2);
res[2] = w.getBlockAt(x1, y2, z1);
res[3] = w.getBlockAt(x1, y2, z2);
res[4] = w.getBlockAt(x2, y1, z1);
res[5] = w.getBlockAt(x2, y1, z2);
res[6] = w.getBlockAt(x2, y2, z1);
res[7] = w.getBlockAt(x2, y2, z2);
res[0] = new Location(w, x1, y1, z1);
res[1] = new Location(w, x1, y1, z2);
res[2] = new Location(w, x1, y2, z1);
res[3] = new Location(w, x1, y2, z2);
res[4] = new Location(w, x2, y1, z1);
res[5] = new Location(w, x2, y1, z2);
res[6] = new Location(w, x2, y2, z1);
res[7] = new Location(w, x2, y2, z2);
return res;
}
@@ -648,11 +648,12 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
* @see java.lang.Iterable#iterator()
*/
@Override
@Deprecated(forRemoval = true)
public Iterator<Block> iterator() {
return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2);
}
public Iterator<Block> chunkedIterator() {
public Iterator<Location> chunkedIterator() {
return new ChunkedCuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2);
}
@@ -751,7 +752,7 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
}
}
public static class ChunkedCuboidIterator implements Iterator<Block> {
public static class ChunkedCuboidIterator implements Iterator<Location> {
private final World w;
private final int minRX, minY, minRZ, maxRX, maxY, maxRZ;
private final int minCX, minCZ, maxCX, maxCZ;
@@ -795,7 +796,7 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
}
@Override
public Block next() {
public Location next() {
if (chunk == null) {
chunk = new Position2(cX, cZ);
if (++cX > maxCX) {
@@ -809,7 +810,7 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
rZ = chunk.getZ() == minCZ ? minRZ : 0;
}
var b = w.getBlockAt((chunk.getX() << 4) + rX, y, (chunk.getZ() << 4) + rZ);
var b = new Location(w, (chunk.getX() << 4) + rX, y, (chunk.getZ() << 4) + rZ);
if (++y > maxY) {
y = minY;
if (++rX > mX) {
@@ -1,77 +1,127 @@
package com.volmit.iris.util.data;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.util.collection.KMap;
import lombok.Data;
import lombok.NonNull;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.SoundGroup;
import org.bukkit.block.*;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.ApiStatus;
import org.bukkit.block.structure.Mirror;
import org.bukkit.block.structure.StructureRotation;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Proxy;
import java.util.*;
@Data
public class IrisCustomData implements BlockData {
private final @NonNull BlockData base;
private final @NotNull Identifier custom;
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 = clazz.getClassLoader();
return (IrisCustomData) Proxy.newProxyInstance(loader, Internal.getInterfaces(loader, clazz), (proxy, method, args) ->
switch (method.getName()) {
case "getBase" -> base;
case "getCustom" -> custom;
case "merge" -> of(base.merge((BlockData) args[0]), custom);
case "clone" -> of(base.clone(), custom);
case "hashCode" -> Objects.hash(base, custom);
case "matches" -> {
if (!(args[0] instanceof IrisCustomData store))
yield false;
yield base.matches(store.getBase()) && custom.equals(store.getCustom());
}
case "equals" -> {
if (!(args[0] instanceof IrisCustomData store))
yield false;
yield store.getBase().equals(base) && store.getCustom().equals(custom);
}
default -> method.invoke(base, args);
});
@NotNull
@Override
public Material getMaterial() {
return base.getMaterial();
}
@ApiStatus.Internal
abstract class Internal {
private static final KMap<Class<?>, Class<?>[]> cache = new KMap<>();
@NotNull
@Override
public String getAsString() {
return base.getAsString();
}
private static Class<?>[] getInterfaces(ClassLoader loader, Class<?> base) {
return cache.computeIfAbsent(base, k -> {
Queue<Class<?>> queue = new LinkedList<>();
Set<Class<?>> set = new HashSet<>();
@NotNull
@Override
public String getAsString(boolean b) {
return base.getAsString(b);
}
queue.add(k);
while (!queue.isEmpty()) {
Class<?> i = queue.poll();
@NotNull
@Override
public BlockData merge(@NotNull BlockData blockData) {
return new IrisCustomData(base.merge(blockData), custom);
}
if (!BlockData.class.isAssignableFrom(i))
continue;
@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();
}
var parent = i.getSuperclass();
if (parent != null)
queue.add(parent);
}
@Override
public int getLightEmission() {
return base.getLightEmission();
}
set.add(IrisCustomData.class);
return set.toArray(Class<?>[]::new);
});
}
@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();
}
}
@@ -1,220 +0,0 @@
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;
}
}
}
@@ -31,8 +31,8 @@ import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.plugin.CommandDummy;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import lombok.Data;
import org.bukkit.entity.Entity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -493,7 +493,10 @@ public class VirtualDecreeCommand {
};
if (getNode().isSync()) {
J.s(rx);
switch (sender.getS()) {
case Entity entity -> Iris.platform.getEntityScheduler(entity).run(rx, null);
case null, default -> Iris.platform.getGlobalScheduler().run(rx);
}
} else {
rx.run();
}
@@ -19,30 +19,21 @@
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint;
import it.unimi.dsi.fastutil.ints.*;
import lombok.Synchronized;
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) + 2;
protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1;
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);
@@ -50,15 +41,10 @@ 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() {
@@ -100,18 +86,13 @@ public class DataContainer<T> {
writeDos(new DataOutputStream(out));
}
@Synchronized
public void writeDos(DataOutputStream dos) throws IOException {
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();
}
Varint.writeUnsignedVarInt(length, dos);
Varint.writeUnsignedVarInt(palette.size(), dos);
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
data.write(dos);
dos.flush();
}
private Palette<T> newPalette(DataInputStream din) throws IOException {
@@ -129,38 +110,25 @@ public class DataContainer<T> {
return new HashPalette<>();
}
@Synchronized
public void set(int position, T t) {
int id;
int id = palette.id(t);
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();
}
write.lock();
try {
if (id == -1) {
id = palette.add(t);
updateBits();
data.set(position, id);
} finally {
write.unlock();
}
data.set(position, id);
}
@Synchronized
@SuppressWarnings("NonAtomicOperationOnVolatileField")
private void updateBits() {
int bits = palette.bits();
if (bits == data.getBits())
if (palette.bits() == data.getBits())
return;
int bits = palette.bits();
if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
palette = newPalette(bits).from(palette);
}
@@ -168,44 +136,18 @@ public class DataContainer<T> {
data = data.setBits(bits);
}
@Synchronized
public T get(int position) {
read.lock();
try {
int id = data.get(position);
int id = data.get(position);
if (id <= 0) {
return null;
}
return palette.get(id);
} finally {
read.unlock();
if (id <= 0) {
return null;
}
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,14 +27,13 @@ import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class HashPalette<T> implements Palette<T> {
private final Object lock = new Object();
private final KMap<T, Integer> palette;
private final LinkedHashMap<T, Integer> palette;
private final KMap<Integer, T> lookup;
private final AtomicInteger size;
public HashPalette() {
this.size = new AtomicInteger(1);
this.palette = new KMap<>();
this.palette = new LinkedHashMap<>();
this.lookup = new KMap<>();
}
@@ -53,13 +52,13 @@ public class HashPalette<T> implements Palette<T> {
return 0;
}
return palette.computeIfAbsent(t, $ -> {
synchronized (lock) {
synchronized (palette) {
return palette.computeIfAbsent(t, $ -> {
int index = size.getAndIncrement();
lookup.put(index, t);
return index;
}
});
});
}
}
@Override
@@ -79,7 +78,7 @@ public class HashPalette<T> implements Palette<T> {
@Override
public void iterate(Consumer2<T, Integer> c) {
synchronized (lock) {
synchronized (palette) {
for (int i = 1; i < size.get(); i++) {
c.accept(lookup.get(i), i);
}
@@ -45,23 +45,25 @@ public class LinearPalette<T> implements Palette<T> {
@Override
public int add(T t) {
if (t == null) {
return 0;
}
int index = size.getAndIncrement();
if (palette.length() <= index)
grow(index);
grow(index + 1);
palette.set(index, t);
return index;
}
private synchronized void grow(int lastIndex) {
if (palette.length() > lastIndex)
return;
private synchronized void grow(int newLength) {
if (newLength > palette.length()) {
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength);
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(lastIndex + 1);
for (int i = 0; i < palette.length(); i++) {
a.set(i, palette.get(i));
for (int i = 0; i < palette.length(); i++) {
a.set(i, palette.get(i));
}
palette = a;
}
palette = a;
}
@Override
@@ -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 - x, z1 - z), k -> noise.noise(x1, z1));
NoiseProvider n = (x1, z1) -> cache.computeIfAbsent(new NoiseKey(x1, z1), k -> noise.noise(k.x, k.z));
if (method.equals(InterpolationMethod.BILINEAR)) {
return getBilinearNoise(x, z, h, n);
@@ -176,8 +176,8 @@ public class Mantle {
* @return the writer
*/
@ChunkCoordinates
public MantleWriter write(EngineMantle engineMantle, int x, int z, int radius, boolean multicore) {
return new MantleWriter(engineMantle, this, x, z, radius, multicore);
public MantleWriter write(EngineMantle engineMantle, int x, int z, int radius) {
return new MantleWriter(engineMantle, this, x, z, radius);
}
/**
@@ -406,6 +406,18 @@ 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
@@ -623,7 +635,7 @@ public class Mantle {
}
public void deleteChunkSlice(int x, int z, Class<?> c) {
if (IrisToolbelt.isRetainingMantleDataForSlice(c.getCanonicalName())) {
if (!IrisToolbelt.toolbeltConfiguration.isEmpty() && IrisToolbelt.toolbeltConfiguration.getOrDefault("retain.mantle." + c.getCanonicalName(), false)) {
return;
}

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