mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-21 11:43:27 +00:00
Merge branch 'dev' into feat/kts
# Conflicts: # core/build.gradle.kts # core/src/main/java/com/volmit/iris/Iris.java # core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java # core/src/main/java/com/volmit/iris/util/io/IO.java # core/src/main/resources/plugin.yml
This commit is contained in:
commit
badf108d56
@ -36,8 +36,11 @@ plugins {
|
|||||||
id("io.sentry.jvm.gradle") version "5.7.0"
|
id("io.sentry.jvm.gradle") version "5.7.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group = "com.volmit"
|
||||||
version = "3.6.11-1.20.1-1.21.5"
|
version = "3.6.11-1.20.1-1.21.5"
|
||||||
|
|
||||||
|
apply<ApiGenerator>()
|
||||||
|
|
||||||
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
||||||
// ======================== WINDOWS =============================
|
// ======================== WINDOWS =============================
|
||||||
registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins")
|
registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins")
|
||||||
@ -63,6 +66,7 @@ val color = "truecolor"
|
|||||||
val errorReporting = false
|
val errorReporting = false
|
||||||
|
|
||||||
val nmsBindings = mapOf(
|
val nmsBindings = mapOf(
|
||||||
|
"v1_21_R5" to "1.21.7-R0.1-SNAPSHOT",
|
||||||
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
|
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
|
||||||
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
|
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
|
||||||
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
|
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
|
||||||
@ -90,6 +94,7 @@ nmsBindings.forEach { key, value ->
|
|||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(project(":core"))
|
compileOnly(project(":core"))
|
||||||
compileOnly("org.jetbrains:annotations:26.0.2")
|
compileOnly("org.jetbrains:annotations:26.0.2")
|
||||||
|
compileOnly("net.bytebuddy:byte-buddy:1.17.5")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +109,8 @@ nmsBindings.forEach { key, value ->
|
|||||||
systemProperty("disable.watchdog", "")
|
systemProperty("disable.watchdog", "")
|
||||||
systemProperty("net.kyori.ansi.colorLevel", color)
|
systemProperty("net.kyori.ansi.colorLevel", color)
|
||||||
systemProperty("com.mojang.eula.agree", true)
|
systemProperty("com.mojang.eula.agree", true)
|
||||||
systemProperty("iris.errorReporting", errorReporting)
|
systemProperty("iris.suppressReporting", !errorReporting)
|
||||||
|
jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +121,7 @@ tasks {
|
|||||||
from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) })
|
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").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) })
|
||||||
|
from(project(":core:agent").tasks.jar.flatMap { it.archiveFile })
|
||||||
archiveFileName.set("Iris-${project.version}.jar")
|
archiveFileName.set("Iris-${project.version}.jar")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +175,6 @@ fun exec(vararg command: Any) {
|
|||||||
p.waitFor()
|
p.waitFor()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":core"))
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations.configureEach {
|
configurations.configureEach {
|
||||||
resolutionStrategy.cacheChangingModulesFor(60, "minutes")
|
resolutionStrategy.cacheChangingModulesFor(60, "minutes")
|
||||||
resolutionStrategy.cacheDynamicVersionsFor(60, "minutes")
|
resolutionStrategy.cacheDynamicVersionsFor(60, "minutes")
|
||||||
@ -193,6 +196,8 @@ allprojects {
|
|||||||
maven("https://repo.mineinabyss.com/releases")
|
maven("https://repo.mineinabyss.com/releases")
|
||||||
maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/")
|
maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/")
|
||||||
maven("https://repo.nexomc.com/releases/")
|
maven("https://repo.nexomc.com/releases/")
|
||||||
|
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
|
||||||
|
maven("https://repo.onarandombox.com/content/groups/public/")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
11
buildSrc/build.gradle.kts
Normal file
11
buildSrc/build.gradle.kts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "2.0.20"
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.ow2.asm:asm:9.8")
|
||||||
|
}
|
121
buildSrc/src/main/kotlin/ApiGenerator.kt
Normal file
121
buildSrc/src/main/kotlin/ApiGenerator.kt
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.publish.PublishingExtension
|
||||||
|
import org.gradle.api.publish.maven.MavenPublication
|
||||||
|
import org.gradle.api.tasks.InputFile
|
||||||
|
import org.gradle.api.tasks.OutputFile
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.jvm.tasks.Jar
|
||||||
|
import org.objectweb.asm.*
|
||||||
|
import java.io.File
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import java.util.jar.JarOutputStream
|
||||||
|
|
||||||
|
class ApiGenerator : Plugin<Project> {
|
||||||
|
override fun apply(target: Project): Unit = with(target) {
|
||||||
|
plugins.apply("maven-publish")
|
||||||
|
val task = tasks.register("irisApi", GenerateApiTask::class.java)
|
||||||
|
extensions.findByType(PublishingExtension::class.java)!!.apply {
|
||||||
|
repositories.maven {
|
||||||
|
it.name = "deployDir"
|
||||||
|
it.url = targetDirectory.toURI()
|
||||||
|
}
|
||||||
|
|
||||||
|
publications.create("maven", MavenPublication::class.java) {
|
||||||
|
it.groupId = name
|
||||||
|
it.version = version.toString()
|
||||||
|
it.artifact(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GenerateApiTask : DefaultTask() {
|
||||||
|
init {
|
||||||
|
group = "iris"
|
||||||
|
dependsOn("jar")
|
||||||
|
finalizedBy("publishMavenPublicationToDeployDirRepository")
|
||||||
|
doLast {
|
||||||
|
logger.lifecycle("The API is located at ${outputFile.absolutePath}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
val inputFile: File = project.tasks
|
||||||
|
.named("jar", Jar::class.java)
|
||||||
|
.get()
|
||||||
|
.archiveFile
|
||||||
|
.get()
|
||||||
|
.asFile
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
val outputFile: File = project.targetDirectory.resolve(inputFile.name)
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun generate() {
|
||||||
|
JarFile(inputFile).use { jar ->
|
||||||
|
JarOutputStream(outputFile.apply { parentFile?.mkdirs() }.outputStream()).use { out ->
|
||||||
|
jar.stream()
|
||||||
|
.parallel()
|
||||||
|
.filter { !it.isDirectory }
|
||||||
|
.filter { it.name.endsWith(".class") }
|
||||||
|
.forEach {
|
||||||
|
val bytes = jar.getInputStream(it).use { input ->
|
||||||
|
val writer = ClassWriter(ClassWriter.COMPUTE_MAXS)
|
||||||
|
val visitor = MethodClearingVisitor(writer)
|
||||||
|
ClassReader(input).accept(visitor, 0)
|
||||||
|
writer.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(out) {
|
||||||
|
out.putNextEntry(it)
|
||||||
|
out.write(bytes)
|
||||||
|
out.closeEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val Project.targetDirectory: File get() {
|
||||||
|
val dir = System.getenv("DEPLOY_DIR") ?: return project.layout.buildDirectory.dir("api").get().asFile
|
||||||
|
return File(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MethodClearingVisitor(
|
||||||
|
cv: ClassVisitor
|
||||||
|
) : ClassVisitor(Opcodes.ASM9, cv) {
|
||||||
|
|
||||||
|
override fun visitMethod(
|
||||||
|
access: Int,
|
||||||
|
name: String?,
|
||||||
|
descriptor: String?,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<out String>?
|
||||||
|
) = ExceptionThrowingMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions))
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExceptionThrowingMethodVisitor(
|
||||||
|
mv: MethodVisitor
|
||||||
|
) : MethodVisitor(Opcodes.ASM9, mv) {
|
||||||
|
|
||||||
|
override fun visitCode() {
|
||||||
|
if (mv == null) return
|
||||||
|
mv.visitCode()
|
||||||
|
|
||||||
|
mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException")
|
||||||
|
mv.visitInsn(Opcodes.DUP)
|
||||||
|
mv.visitLdcInsn("Only API")
|
||||||
|
mv.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
"java/lang/IllegalStateException",
|
||||||
|
"<init>", "(Ljava/lang/String;)V", false
|
||||||
|
)
|
||||||
|
mv.visitInsn(Opcodes.ATHROW)
|
||||||
|
|
||||||
|
mv.visitMaxs(0, 0)
|
||||||
|
mv.visitEnd()
|
||||||
|
}
|
||||||
|
}
|
12
core/agent/build.gradle.kts
Normal file
12
core/agent/build.gradle.kts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
plugins {
|
||||||
|
java
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.jar {
|
||||||
|
manifest.attributes(
|
||||||
|
"Agent-Class" to "com.volmit.iris.util.agent.Installer",
|
||||||
|
"Premain-Class" to "com.volmit.iris.util.agent.Installer",
|
||||||
|
"Can-Redefine-Classes" to true,
|
||||||
|
"Can-Retransform-Classes" to true
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.volmit.iris.util.agent;
|
||||||
|
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
|
||||||
|
public class Installer {
|
||||||
|
private static volatile Instrumentation instrumentation;
|
||||||
|
|
||||||
|
public static Instrumentation getInstrumentation() {
|
||||||
|
Instrumentation instrumentation = Installer.instrumentation;
|
||||||
|
if (instrumentation == null) {
|
||||||
|
throw new IllegalStateException("The agent is not loaded or this method is not called via the system class loader");
|
||||||
|
}
|
||||||
|
return instrumentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void premain(String arguments, Instrumentation instrumentation) {
|
||||||
|
doMain(instrumentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void agentmain(String arguments, Instrumentation instrumentation) {
|
||||||
|
doMain(instrumentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void doMain(Instrumentation instrumentation) {
|
||||||
|
if (Installer.instrumentation != null)
|
||||||
|
return;
|
||||||
|
Installer.instrumentation = instrumentation;
|
||||||
|
}
|
||||||
|
}
|
@ -26,10 +26,8 @@ plugins {
|
|||||||
val apiVersion = "1.19"
|
val apiVersion = "1.19"
|
||||||
val main = "com.volmit.iris.Iris"
|
val main = "com.volmit.iris.Iris"
|
||||||
|
|
||||||
repositories {
|
val dynamic: Configuration by configurations.creating
|
||||||
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
|
configurations.compileOnly { extendsFrom(dynamic) }
|
||||||
maven("https://repo.auxilor.io/repository/maven-public/")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dependencies.
|
* Dependencies.
|
||||||
@ -48,10 +46,6 @@ dependencies {
|
|||||||
compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT")
|
compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT")
|
||||||
compileOnly("org.apache.logging.log4j:log4j-api:2.19.0")
|
compileOnly("org.apache.logging.log4j:log4j-api:2.19.0")
|
||||||
compileOnly("org.apache.logging.log4j:log4j-core:2.19.0")
|
compileOnly("org.apache.logging.log4j:log4j-core:2.19.0")
|
||||||
compileOnly("commons-io:commons-io:2.13.0")
|
|
||||||
compileOnly("commons-lang:commons-lang:2.6")
|
|
||||||
compileOnly("com.github.oshi:oshi-core:5.8.5")
|
|
||||||
compileOnly("org.lz4:lz4-java:1.8.0")
|
|
||||||
|
|
||||||
// Third Party Integrations
|
// Third Party Integrations
|
||||||
compileOnly("com.nexomc:nexo:1.6.0")
|
compileOnly("com.nexomc:nexo:1.6.0")
|
||||||
@ -60,9 +54,14 @@ dependencies {
|
|||||||
compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8")
|
compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8")
|
||||||
compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT")
|
compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT")
|
||||||
compileOnly("com.willfp:EcoItems:5.44.0")
|
compileOnly("com.willfp:EcoItems:5.44.0")
|
||||||
|
compileOnly("io.lumine:Mythic-Dist:5.2.1")
|
||||||
|
compileOnly("io.lumine:MythicCrucible-Dist:2.0.0")
|
||||||
|
compileOnly("me.kryniowesegryderiusz:kgenerators-core:7.3") {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
compileOnly("org.mvplugins.multiverse.core:multiverse-core:5.1.0")
|
||||||
//implementation files("libs/CustomItems.jar")
|
//implementation files("libs/CustomItems.jar")
|
||||||
|
|
||||||
|
|
||||||
// Shaded
|
// Shaded
|
||||||
implementation("com.dfsek:paralithic:0.8.1")
|
implementation("com.dfsek:paralithic:0.8.1")
|
||||||
implementation("io.papermc:paperlib:1.0.5")
|
implementation("io.papermc:paperlib:1.0.5")
|
||||||
@ -71,23 +70,21 @@ dependencies {
|
|||||||
implementation("net.kyori:adventure-api:4.17.0")
|
implementation("net.kyori:adventure-api:4.17.0")
|
||||||
implementation("org.bstats:bstats-bukkit:3.1.0")
|
implementation("org.bstats:bstats-bukkit:3.1.0")
|
||||||
|
|
||||||
//implementation("org.bytedeco:javacpp:1.5.10")
|
|
||||||
//implementation("org.bytedeco:cuda-platform:12.3-8.9-1.5.10")
|
|
||||||
compileOnly("io.lumine:Mythic-Dist:5.2.1")
|
|
||||||
compileOnly("io.lumine:MythicCrucible-Dist:2.0.0")
|
|
||||||
|
|
||||||
// Dynamically Loaded
|
// Dynamically Loaded
|
||||||
compileOnly("io.timeandspace:smoothie-map:2.0.2")
|
dynamic("commons-io:commons-io:2.13.0")
|
||||||
compileOnly("it.unimi.dsi:fastutil:8.5.8")
|
dynamic("commons-lang:commons-lang:2.6")
|
||||||
compileOnly("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2")
|
dynamic("com.github.oshi:oshi-core:6.6.5")
|
||||||
compileOnly("org.zeroturnaround:zt-zip:1.14")
|
dynamic("org.lz4:lz4-java:1.8.0")
|
||||||
compileOnly("com.google.code.gson:gson:2.10.1")
|
dynamic("it.unimi.dsi:fastutil:8.5.8")
|
||||||
compileOnly("org.ow2.asm:asm:9.2")
|
dynamic("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2")
|
||||||
compileOnly("com.google.guava:guava:33.0.0-jre")
|
dynamic("org.zeroturnaround:zt-zip:1.14")
|
||||||
compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.6")
|
dynamic("com.google.code.gson:gson:2.10.1")
|
||||||
compileOnly("org.apache.commons:commons-lang3:3.12.0")
|
dynamic("org.ow2.asm:asm:9.8")
|
||||||
compileOnly("com.github.oshi:oshi-core:6.6.5")
|
dynamic("com.github.ben-manes.caffeine:caffeine:3.0.6")
|
||||||
compileOnly("org.dom4j:dom4j:2.1.4")
|
dynamic("org.apache.commons:commons-lang3:3.12.0")
|
||||||
|
dynamic("net.bytebuddy:byte-buddy:1.17.5")
|
||||||
|
dynamic("net.bytebuddy:byte-buddy-agent:1.17.5")
|
||||||
|
dynamic("org.dom4j:dom4j:2.1.4")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -119,7 +116,8 @@ tasks {
|
|||||||
"name" to rootProject.name,
|
"name" to rootProject.name,
|
||||||
"version" to rootProject.version,
|
"version" to rootProject.version,
|
||||||
"apiVersion" to apiVersion,
|
"apiVersion" to apiVersion,
|
||||||
"main" to main
|
"main" to main,
|
||||||
|
"libraries" to dynamic.allDependencies.map { "\n - $it" }.sorted().joinToString("")
|
||||||
)
|
)
|
||||||
filesMatching("**/plugin.yml") {
|
filesMatching("**/plugin.yml") {
|
||||||
expand(inputs.properties)
|
expand(inputs.properties)
|
||||||
|
@ -36,11 +36,9 @@ import com.volmit.iris.core.service.StudioSVC;
|
|||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.EnginePanic;
|
import com.volmit.iris.engine.EnginePanic;
|
||||||
import com.volmit.iris.engine.object.IrisCompat;
|
import com.volmit.iris.engine.object.IrisCompat;
|
||||||
import com.volmit.iris.engine.object.IrisContextInjector;
|
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
import com.volmit.iris.engine.object.IrisWorld;
|
import com.volmit.iris.engine.object.IrisWorld;
|
||||||
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
|
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
|
||||||
import com.volmit.iris.engine.platform.DummyChunkGenerator;
|
|
||||||
import com.volmit.iris.core.safeguard.IrisSafeguard;
|
import com.volmit.iris.core.safeguard.IrisSafeguard;
|
||||||
import com.volmit.iris.core.safeguard.UtilsSFG;
|
import com.volmit.iris.core.safeguard.UtilsSFG;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
@ -72,6 +70,7 @@ import com.volmit.iris.util.sentry.IrisLogger;
|
|||||||
import com.volmit.iris.util.sentry.ServerID;
|
import com.volmit.iris.util.sentry.ServerID;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
import io.sentry.Sentry;
|
import io.sentry.Sentry;
|
||||||
|
import lombok.NonNull;
|
||||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||||
import org.bstats.bukkit.Metrics;
|
import org.bstats.bukkit.Metrics;
|
||||||
@ -466,15 +465,13 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
setupAudience();
|
setupAudience();
|
||||||
setupSentry();
|
setupSentry();
|
||||||
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
|
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
|
||||||
INMS.get();
|
|
||||||
IO.delete(new File("iris"));
|
IO.delete(new File("iris"));
|
||||||
compat = IrisCompat.configured(getDataFile("compat.json"));
|
compat = IrisCompat.configured(getDataFile("compat.json"));
|
||||||
ServerConfigurator.configure();
|
ServerConfigurator.configure();
|
||||||
new IrisContextInjector();
|
|
||||||
ExecutionEnvironment.createSimple();
|
ExecutionEnvironment.createSimple();
|
||||||
IrisSafeguard.IrisSafeguardSystem();
|
IrisSafeguard.IrisSafeguardSystem();
|
||||||
getSender().setTag(getTag());
|
getSender().setTag(getTag());
|
||||||
IrisSafeguard.earlySplash();
|
IrisSafeguard.splash(true);
|
||||||
linkMultiverseCore = new MultiverseCoreLink();
|
linkMultiverseCore = new MultiverseCoreLink();
|
||||||
linkMythicMobs = new MythicMobsLink();
|
linkMythicMobs = new MythicMobsLink();
|
||||||
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
||||||
@ -489,8 +486,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
J.sr(this::tickQueue, 0);
|
J.sr(this::tickQueue, 0);
|
||||||
J.s(this::setupPapi);
|
J.s(this::setupPapi);
|
||||||
J.a(ServerConfigurator::configure, 20);
|
J.a(ServerConfigurator::configure, 20);
|
||||||
splash();
|
IrisSafeguard.splash(false);
|
||||||
UtilsSFG.splash();
|
|
||||||
|
|
||||||
autoStartStudio();
|
autoStartStudio();
|
||||||
checkForBukkitWorlds();
|
checkForBukkitWorlds();
|
||||||
@ -761,37 +757,11 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
@Override
|
@Override
|
||||||
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
||||||
Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id);
|
Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id);
|
||||||
if (worldName.equals("test")) {
|
if (id == null || id.isEmpty()) id = IrisSettings.get().getGenerator().getDefaultWorldType();
|
||||||
try {
|
|
||||||
throw new RuntimeException();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.info(e.getStackTrace()[1].getClassName());
|
|
||||||
if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) {
|
|
||||||
Iris.debug("MVC Test detected, Quick! Send them the dummy!");
|
|
||||||
return new DummyChunkGenerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IrisDimension dim;
|
|
||||||
if (id == null || id.isEmpty()) {
|
|
||||||
dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType());
|
|
||||||
} else {
|
|
||||||
dim = IrisData.loadAnyDimension(id);
|
|
||||||
}
|
|
||||||
Iris.debug("Generator ID: " + id + " requested by bukkit/plugin");
|
Iris.debug("Generator ID: " + id + " requested by bukkit/plugin");
|
||||||
|
IrisDimension dim = loadDimension(worldName, id);
|
||||||
if (dim == null) {
|
|
||||||
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
|
|
||||||
|
|
||||||
service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true);
|
|
||||||
dim = IrisData.loadAnyDimension(id);
|
|
||||||
|
|
||||||
if (dim == null) {
|
if (dim == null) {
|
||||||
throw new RuntimeException("Can't find dimension " + id + "!");
|
throw new RuntimeException("Can't find dimension " + id + "!");
|
||||||
} else {
|
|
||||||
Iris.info("Resolved missing dimension, proceeding with generation.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Iris.debug("Assuming IrisDimension: " + dim.getName());
|
Iris.debug("Assuming IrisDimension: " + dim.getName());
|
||||||
@ -816,6 +786,24 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey());
|
return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static IrisDimension loadDimension(@NonNull String worldName, @NonNull String id) {
|
||||||
|
var data = IrisData.get(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pack")));
|
||||||
|
var dimension = data.getDimensionLoader().load(id);
|
||||||
|
if (dimension == null) dimension = IrisData.loadAnyDimension(id);
|
||||||
|
if (dimension == null) {
|
||||||
|
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
|
||||||
|
Iris.service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false);
|
||||||
|
dimension = IrisData.loadAnyDimension(id);
|
||||||
|
|
||||||
|
if (dimension != null) {
|
||||||
|
Iris.info("Resolved missing dimension, proceeding.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimension;
|
||||||
|
}
|
||||||
|
|
||||||
public void splash() {
|
public void splash() {
|
||||||
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
|
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
|
||||||
return;
|
return;
|
||||||
@ -958,7 +946,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
|
|
||||||
private static void setupSentry() {
|
private static void setupSentry() {
|
||||||
var settings = IrisSettings.get().getSentry();
|
var settings = IrisSettings.get().getSentry();
|
||||||
if (settings.disableAutoReporting || Sentry.isEnabled() || !Boolean.getBoolean("iris.errorReporting")) return;
|
if (settings.disableAutoReporting || Sentry.isEnabled() || Boolean.getBoolean("iris.suppressReporting")) return;
|
||||||
Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings.");
|
Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings.");
|
||||||
Iris.info("Your server ID is: " + ServerID.ID);
|
Iris.info("Your server ID is: " + ServerID.ID);
|
||||||
Sentry.init(options -> {
|
Sentry.init(options -> {
|
||||||
@ -977,7 +965,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
event.setTag("iris.nms", INMS.get().getClass().getCanonicalName());
|
event.setTag("iris.nms", INMS.get().getClass().getCanonicalName());
|
||||||
var context = IrisContext.get();
|
var context = IrisContext.get();
|
||||||
if (context != null) event.getContexts().set("engine", context.asContext());
|
if (context != null) event.getContexts().set("engine", context.asContext());
|
||||||
event.getContexts().set("safeguard", ServerBootSFG.allIncompatibilities);
|
event.getContexts().set("safeguard", IrisSafeguard.asContext());
|
||||||
return event;
|
return event;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -150,7 +150,7 @@ public class IrisSettings {
|
|||||||
public boolean useCacheByDefault = true;
|
public boolean useCacheByDefault = true;
|
||||||
public boolean useHighPriority = false;
|
public boolean useHighPriority = false;
|
||||||
public boolean useVirtualThreads = false;
|
public boolean useVirtualThreads = false;
|
||||||
public boolean useTicketQueue = false;
|
public boolean useTicketQueue = true;
|
||||||
public int maxConcurrency = 256;
|
public int maxConcurrency = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
79
core/src/main/java/com/volmit/iris/core/IrisWorlds.java
Normal file
79
core/src/main/java/com/volmit/iris/core/IrisWorlds.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package com.volmit.iris.core;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.io.IO;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class IrisWorlds {
|
||||||
|
private static final AtomicCache<IrisWorlds> cache = new AtomicCache<>();
|
||||||
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
private static final Type TYPE = TypeToken.getParameterized(KMap.class, String.class, String.class).getType();
|
||||||
|
private final KMap<String, String> worlds;
|
||||||
|
private volatile boolean dirty = false;
|
||||||
|
|
||||||
|
private IrisWorlds(KMap<String, String> worlds) {
|
||||||
|
this.worlds = worlds;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IrisWorlds get() {
|
||||||
|
return cache.aquire(() -> {
|
||||||
|
File file = Iris.instance.getDataFile("worlds.json");
|
||||||
|
if (!file.exists()) {
|
||||||
|
return new IrisWorlds(new KMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String json = IO.readAll(file);
|
||||||
|
KMap<String, String> worlds = GSON.fromJson(json, TYPE);
|
||||||
|
return new IrisWorlds(Objects.requireNonNullElseGet(worlds, KMap::new));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to load worlds.json!");
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.reportError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IrisWorlds(new KMap<>());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String name, String type) {
|
||||||
|
String old = worlds.put(name, type);
|
||||||
|
if (!type.equals(old))
|
||||||
|
dirty = true;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<File> getFolders() {
|
||||||
|
return worlds.keySet().stream().map(k -> new File(Bukkit.getWorldContainer(), k));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
dirty = worlds.entrySet().removeIf(entry -> !new File(Bukkit.getWorldContainer(), entry.getKey() + "/iris/pack/dimensions/" + entry.getValue() + ".json").exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void save() {
|
||||||
|
clean();
|
||||||
|
if (!dirty) return;
|
||||||
|
try {
|
||||||
|
IO.write(Iris.instance.getDataFile("worlds.json"), OutputStreamWriter::new, writer -> GSON.toJson(worlds, TYPE, writer));
|
||||||
|
dirty = false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.error("Failed to save worlds.json!");
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,10 +23,7 @@ import com.volmit.iris.core.loader.IrisData;
|
|||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
||||||
import com.volmit.iris.engine.object.IrisBiome;
|
import com.volmit.iris.engine.object.*;
|
||||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
|
||||||
import com.volmit.iris.engine.object.IrisRange;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
@ -34,8 +31,8 @@ import com.volmit.iris.util.format.C;
|
|||||||
import com.volmit.iris.util.misc.ServerProperties;
|
import com.volmit.iris.util.misc.ServerProperties;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
@ -45,12 +42,12 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.volmit.iris.core.nms.datapack.IDataFixer.Dimension.*;
|
|
||||||
|
|
||||||
public class ServerConfigurator {
|
public class ServerConfigurator {
|
||||||
public static void configure() {
|
public static void configure() {
|
||||||
IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration();
|
IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration();
|
||||||
@ -110,19 +107,25 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void installDataPacks(IDataFixer fixer, boolean fullInstall) {
|
public static void installDataPacks(IDataFixer fixer, boolean fullInstall) {
|
||||||
|
if (fixer == null) {
|
||||||
|
Iris.error("Unable to install datapacks, fixer is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
Iris.info("Checking Data Packs...");
|
Iris.info("Checking Data Packs...");
|
||||||
DimensionHeight height = new DimensionHeight(fixer);
|
DimensionHeight height = new DimensionHeight(fixer);
|
||||||
KList<File> folders = getDatapacksFolder();
|
KList<File> folders = getDatapacksFolder();
|
||||||
KMap<String, KSet<String>> biomes = new KMap<>();
|
KMap<String, KSet<String>> biomes = new KMap<>();
|
||||||
|
|
||||||
allPacks().flatMap(height::merge)
|
try (Stream<IrisData> stream = allPacks()) {
|
||||||
|
stream.flatMap(height::merge)
|
||||||
.parallel()
|
.parallel()
|
||||||
.forEach(dim -> {
|
.forEach(dim -> {
|
||||||
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
|
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
|
||||||
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
|
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
|
||||||
|
dim.installDimensionType(fixer, folders);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
IrisDimension.writeShared(folders, height);
|
IrisDimension.writeShared(folders, height);
|
||||||
|
|
||||||
Iris.info("Data Packs Setup!");
|
Iris.info("Data Packs Setup!");
|
||||||
|
|
||||||
if (fullInstall)
|
if (fullInstall)
|
||||||
@ -130,7 +133,8 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void verifyDataPacksPost(boolean allowRestarting) {
|
private static void verifyDataPacksPost(boolean allowRestarting) {
|
||||||
boolean bad = allPacks()
|
try (Stream<IrisData> stream = allPacks()) {
|
||||||
|
boolean bad = stream
|
||||||
.map(data -> {
|
.map(data -> {
|
||||||
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
|
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
|
||||||
var loader = data.getDimensionLoader();
|
var loader = data.getDimensionLoader();
|
||||||
@ -143,6 +147,7 @@ public class ServerConfigurator {
|
|||||||
.toList()
|
.toList()
|
||||||
.contains(true);
|
.contains(true);
|
||||||
if (!bad) return;
|
if (!bad) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (allowRestarting) {
|
if (allowRestarting) {
|
||||||
@ -216,6 +221,11 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (INMS.get().missingDimensionTypes(dimension.getDimensionTypeKey())) {
|
||||||
|
Iris.warn("The Dimension Type for " + dimension.getLoadFile() + " is not registered on the server.");
|
||||||
|
warn = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (warn) {
|
if (warn) {
|
||||||
Iris.error("The Pack " + key + " is INCAPABLE of generating custom biomes");
|
Iris.error("The Pack " + key + " is INCAPABLE of generating custom biomes");
|
||||||
Iris.error("If not done automatically, restart your server before generating with this pack!");
|
Iris.error("If not done automatically, restart your server before generating with this pack!");
|
||||||
@ -225,9 +235,13 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<IrisData> allPacks() {
|
public static Stream<IrisData> allPacks() {
|
||||||
return Stream.concat(listFiles(new File("plugins/Iris/packs")),
|
return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")),
|
||||||
listFiles(Bukkit.getWorldContainer()).map(w -> new File(w, "iris/pack")))
|
IrisWorlds.get().getFolders().map(w -> new File(w, "iris/pack")))
|
||||||
.filter(File::isDirectory)
|
.filter(File::isDirectory)
|
||||||
|
.filter( base -> {
|
||||||
|
var content = new File(base, "dimensions").listFiles();
|
||||||
|
return content != null && content.length > 0;
|
||||||
|
})
|
||||||
.map(IrisData::get);
|
.map(IrisData::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,20 +256,24 @@ public class ServerConfigurator {
|
|||||||
return path.substring(worldContainer.length(), path.length() - l);
|
return path.substring(worldContainer.length(), path.length() - l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
private static Stream<File> listFiles(File parent) {
|
private static Stream<File> listFiles(File parent) {
|
||||||
var files = parent.listFiles();
|
if (!parent.isDirectory()) return Stream.empty();
|
||||||
return files == null ? Stream.empty() : Arrays.stream(files);
|
return Files.walk(parent.toPath()).map(Path::toFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class DimensionHeight {
|
public static class DimensionHeight {
|
||||||
private final IDataFixer fixer;
|
private final IDataFixer fixer;
|
||||||
private IrisRange overworld = new IrisRange();
|
private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3];
|
||||||
private IrisRange nether = new IrisRange();
|
|
||||||
private IrisRange end = new IrisRange();
|
public DimensionHeight(IDataFixer fixer) {
|
||||||
private int logicalOverworld = 0;
|
this.fixer = fixer;
|
||||||
private int logicalNether = 0;
|
for (int i = 0; i < 3; i++) {
|
||||||
private int logicalEnd = 0;
|
dimensions[i] = new AtomicIntegerArray(new int[]{
|
||||||
|
Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Stream<IrisDimension> merge(IrisData data) {
|
public Stream<IrisDimension> merge(IrisData data) {
|
||||||
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
|
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
|
||||||
@ -266,25 +284,29 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void merge(IrisDimension dimension) {
|
public void merge(IrisDimension dimension) {
|
||||||
overworld.merge(dimension.getDimensionHeight());
|
AtomicIntegerArray array = dimensions[dimension.getBaseDimension().ordinal()];
|
||||||
nether.merge(dimension.getDimensionHeight());
|
array.updateAndGet(0, min -> Math.min(min, dimension.getMinHeight()));
|
||||||
end.merge(dimension.getDimensionHeight());
|
array.updateAndGet(1, max -> Math.max(max, dimension.getMaxHeight()));
|
||||||
|
array.updateAndGet(2, logical -> Math.max(logical, dimension.getLogicalHeight()));
|
||||||
logicalOverworld = Math.max(logicalOverworld, dimension.getLogicalHeight());
|
|
||||||
logicalNether = Math.max(logicalNether, dimension.getLogicalHeightNether());
|
|
||||||
logicalEnd = Math.max(logicalEnd, dimension.getLogicalHeightEnd());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String overworldType() {
|
public String[] jsonStrings() {
|
||||||
return fixer.createDimension(OVERRWORLD, overworld, logicalOverworld).toString(4);
|
var dims = IDataFixer.Dimension.values();
|
||||||
|
var arr = new String[3];
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
arr[i] = jsonString(dims[i]);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String netherType() {
|
public String jsonString(IDataFixer.Dimension dimension) {
|
||||||
return fixer.createDimension(NETHER, nether, logicalNether).toString(4);
|
var data = dimensions[dimension.ordinal()];
|
||||||
}
|
int minY = data.get(0);
|
||||||
|
int maxY = data.get(1);
|
||||||
public String endType() {
|
int logicalHeight = data.get(2);
|
||||||
return fixer.createDimension(THE_END, end, logicalEnd).toString(4);
|
if (minY == Integer.MAX_VALUE || maxY == Integer.MIN_VALUE || Integer.MIN_VALUE == logicalHeight)
|
||||||
|
return null;
|
||||||
|
return fixer.createDimension(dimension, minY, maxY - minY, logicalHeight, null).toString(4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.volmit.iris.core.link;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.service.ExternalDataSVC;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.data.B;
|
||||||
|
import com.volmit.iris.util.data.IrisCustomData;
|
||||||
|
import me.kryniowesegryderiusz.kgenerators.Main;
|
||||||
|
import me.kryniowesegryderiusz.kgenerators.api.KGeneratorsAPI;
|
||||||
|
import me.kryniowesegryderiusz.kgenerators.generators.locations.objects.GeneratorLocation;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.MissingResourceException;
|
||||||
|
|
||||||
|
public class KGeneratorsDataProvider extends ExternalDataProvider {
|
||||||
|
public KGeneratorsDataProvider() {
|
||||||
|
super("KGenerators");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) throws MissingResourceException {
|
||||||
|
if (Main.getGenerators().get(blockId.key()) == null) throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
|
||||||
|
return new IrisCustomData(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) throws MissingResourceException {
|
||||||
|
var gen = Main.getGenerators().get(itemId.key());
|
||||||
|
if (gen == null) throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key());
|
||||||
|
return gen.getGeneratorItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) {
|
||||||
|
if (block.getType() != Material.STRUCTURE_VOID) return;
|
||||||
|
var existing = KGeneratorsAPI.getLoadedGeneratorLocation(block.getLocation());
|
||||||
|
if (existing != null) return;
|
||||||
|
block.setBlockData(B.getAir(), false);
|
||||||
|
var gen = Main.getGenerators().get(blockId.key());
|
||||||
|
if (gen == null) return;
|
||||||
|
var loc = new GeneratorLocation(-1, gen, block.getLocation(), Main.getPlacedGenerators().getChunkInfo(block.getChunk()), null, null);
|
||||||
|
Main.getDatabases().getDb().saveGenerator(loc);
|
||||||
|
Main.getPlacedGenerators().addLoaded(loc);
|
||||||
|
Main.getSchedules().schedule(loc, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Identifier[] getBlockTypes() {
|
||||||
|
return Main.getGenerators().getAll().stream()
|
||||||
|
.map(gen -> new Identifier("kgenerators", gen.getId()))
|
||||||
|
.filter(i -> {
|
||||||
|
try {
|
||||||
|
return getBlockData(i) != null;
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toArray(Identifier[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Identifier[] getItemTypes() {
|
||||||
|
return Main.getGenerators().getAll().stream()
|
||||||
|
.map(gen -> new Identifier("kgenerators", gen.getId()))
|
||||||
|
.filter(i -> {
|
||||||
|
try {
|
||||||
|
return getItemStack(i) != null;
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toArray(Identifier[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidProvider(@NotNull Identifier id, boolean isItem) {
|
||||||
|
return "kgenerators".equalsIgnoreCase(id.namespace());
|
||||||
|
}
|
||||||
|
}
|
@ -18,126 +18,60 @@
|
|||||||
|
|
||||||
package com.volmit.iris.core.link;
|
package com.volmit.iris.core.link;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import lombok.SneakyThrows;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.WorldType;
|
import org.mvplugins.multiverse.core.MultiverseCoreApi;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||||
|
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||||
import java.lang.reflect.Field;
|
import org.mvplugins.multiverse.core.world.options.ImportWorldOptions;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class MultiverseCoreLink {
|
public class MultiverseCoreLink {
|
||||||
private final KMap<String, String> worldNameTypes = new KMap<>();
|
private final boolean active;
|
||||||
|
|
||||||
public MultiverseCoreLink() {
|
public MultiverseCoreLink() {
|
||||||
|
active = Bukkit.getPluginManager().getPlugin("Multiverse-Core") != null;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addWorld(String worldName, IrisDimension dim, String seed) {
|
|
||||||
if (!isSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Plugin p = getMultiverse();
|
|
||||||
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
|
|
||||||
Method m = mvWorldManager.getClass().getDeclaredMethod("addWorld",
|
|
||||||
|
|
||||||
String.class, World.Environment.class, String.class, WorldType.class, Boolean.class, String.class, boolean.class);
|
|
||||||
boolean b = (boolean) m.invoke(mvWorldManager, worldName, dim.getEnvironment(), seed, WorldType.NORMAL, false, "Iris", false);
|
|
||||||
saveConfig();
|
|
||||||
return b;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Map<String, ?> getList() {
|
|
||||||
try {
|
|
||||||
Plugin p = getMultiverse();
|
|
||||||
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
|
|
||||||
Field f = mvWorldManager.getClass().getDeclaredField("worldsFromTheConfig");
|
|
||||||
f.setAccessible(true);
|
|
||||||
return (Map<String, ?>) f.get(mvWorldManager);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeFromConfig(World world) {
|
public void removeFromConfig(World world) {
|
||||||
if (!isSupported()) {
|
removeFromConfig(world.getName());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getList().remove(world.getName());
|
|
||||||
saveConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeFromConfig(String world) {
|
public void removeFromConfig(String world) {
|
||||||
if (!isSupported()) {
|
if (!active) return;
|
||||||
return;
|
var manager = worldManager();
|
||||||
|
manager.removeWorld(world).onSuccess(manager::saveWorldsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
getList().remove(world);
|
@SneakyThrows
|
||||||
saveConfig();
|
public void updateWorld(World bukkitWorld, String pack) {
|
||||||
|
if (!active) return;
|
||||||
|
var generator = "Iris:" + pack;
|
||||||
|
var manager = worldManager();
|
||||||
|
var world = manager.getWorld(bukkitWorld).getOrElse(() -> {
|
||||||
|
var options = ImportWorldOptions.worldName(bukkitWorld.getName())
|
||||||
|
.generator(generator)
|
||||||
|
.environment(bukkitWorld.getEnvironment())
|
||||||
|
.useSpawnAdjust(false);
|
||||||
|
return manager.importWorld(options).get();
|
||||||
|
});
|
||||||
|
|
||||||
|
world.setAutoLoad(false);
|
||||||
|
if (!generator.equals(world.getGenerator())) {
|
||||||
|
var field = MultiverseWorld.class.getDeclaredField("worldConfig");
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var config = field.get(world);
|
||||||
|
config.getClass()
|
||||||
|
.getDeclaredMethod("setGenerator", String.class)
|
||||||
|
.invoke(config, generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveConfig() {
|
manager.saveWorldsConfig();
|
||||||
try {
|
|
||||||
Plugin p = getMultiverse();
|
|
||||||
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
|
|
||||||
mvWorldManager.getClass().getDeclaredMethod("saveWorldsConfig").invoke(mvWorldManager);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignWorldType(String worldName, String type) {
|
private WorldManager worldManager() {
|
||||||
worldNameTypes.put(worldName, type);
|
var api = MultiverseCoreApi.get();
|
||||||
}
|
return api.getWorldManager();
|
||||||
|
|
||||||
public String getWorldNameType(String worldName, String defaultType) {
|
|
||||||
try {
|
|
||||||
String t = worldNameTypes.get(worldName);
|
|
||||||
return t == null ? defaultType : t;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
return defaultType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSupported() {
|
|
||||||
return getMultiverse() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Plugin getMultiverse() {
|
|
||||||
|
|
||||||
return Bukkit.getPluginManager().getPlugin("Multiverse-Core");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String envName(World.Environment environment) {
|
|
||||||
if (environment == null) {
|
|
||||||
return "normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (environment) {
|
|
||||||
case NORMAL -> "normal";
|
|
||||||
case NETHER -> "nether";
|
|
||||||
case THE_END -> "end";
|
|
||||||
default -> environment.toString().toLowerCase();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ import lombok.Data;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@ -369,6 +371,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
for (ResourceLoader<?> i : loaders.values()) {
|
for (ResourceLoader<?> i : loaders.values()) {
|
||||||
i.clearList();
|
i.clearList();
|
||||||
}
|
}
|
||||||
|
possibleSnippets.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toLoadKey(File f) {
|
public String toLoadKey(File f) {
|
||||||
@ -435,8 +438,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
File f = new File(getDataFolder(), r + ".json");
|
File f = new File(getDataFolder(), r + ".json");
|
||||||
|
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
try {
|
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
|
||||||
JsonReader snippetReader = new JsonReader(new FileReader(f));
|
|
||||||
return adapter.read(snippetReader);
|
return adapter.read(snippetReader);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.error("Couldn't read snippet " + r + " in " + reader.getPath() + " (" + e.getMessage() + ")");
|
Iris.error("Couldn't read snippet " + r + " in " + reader.getPath() + " (" + e.getMessage() + ")");
|
||||||
@ -470,11 +472,20 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
|||||||
KList<String> l = new KList<>();
|
KList<String> l = new KList<>();
|
||||||
|
|
||||||
File snippetFolder = new File(getDataFolder(), "snippet/" + f);
|
File snippetFolder = new File(getDataFolder(), "snippet/" + f);
|
||||||
|
if (!snippetFolder.exists()) return l;
|
||||||
|
|
||||||
if (snippetFolder.exists() && snippetFolder.isDirectory()) {
|
String absPath = snippetFolder.getAbsolutePath();
|
||||||
for (File i : snippetFolder.listFiles()) {
|
try (var stream = Files.walk(snippetFolder.toPath())) {
|
||||||
l.add("snippet/" + f + "/" + i.getName().split("\\Q.\\E")[0]);
|
stream.filter(Files::isRegularFile)
|
||||||
}
|
.map(Path::toAbsolutePath)
|
||||||
|
.map(Path::toString)
|
||||||
|
.filter(s -> s.endsWith(".json"))
|
||||||
|
.map(s -> s.substring(absPath.length() + 1))
|
||||||
|
.map(s -> s.replace("\\", "/"))
|
||||||
|
.map(s -> s.split("\\Q.\\E")[0])
|
||||||
|
.forEach(s -> l.add("snippet/" + f + "/" + s));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
|
@ -35,7 +35,9 @@ public class INMS {
|
|||||||
"1.21.2", "v1_21_R2",
|
"1.21.2", "v1_21_R2",
|
||||||
"1.21.3", "v1_21_R2",
|
"1.21.3", "v1_21_R2",
|
||||||
"1.21.4", "v1_21_R3",
|
"1.21.4", "v1_21_R3",
|
||||||
"1.21.5", "v1_21_R4"
|
"1.21.5", "v1_21_R4",
|
||||||
|
"1.21.6", "v1_21_R5",
|
||||||
|
"1.21.7", "v1_21_R5"
|
||||||
);
|
);
|
||||||
private static final List<Version> PACKS = List.of(
|
private static final List<Version> PACKS = List.of(
|
||||||
new Version(21, 4, "31020"),
|
new Version(21, 4, "31020"),
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
package com.volmit.iris.core.nms;
|
package com.volmit.iris.core.nms;
|
||||||
|
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
@ -36,6 +36,7 @@ import org.bukkit.entity.EntityType;
|
|||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
|
||||||
@ -89,14 +90,11 @@ public interface INMSBinding {
|
|||||||
MCABiomeContainer newBiomeContainer(int min, int max);
|
MCABiomeContainer newBiomeContainer(int min, int max);
|
||||||
|
|
||||||
default World createWorld(WorldCreator c) {
|
default World createWorld(WorldCreator c) {
|
||||||
if (missingDimensionTypes(true, true, true))
|
if (c.generator() instanceof PlatformChunkGenerator gen
|
||||||
throw new IllegalStateException("Missing dimenstion types to create world");
|
&& missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey()))
|
||||||
|
throw new IllegalStateException("Missing dimension types to create world");
|
||||||
try (var ignored = injectLevelStems()) {
|
|
||||||
ignored.storeContext();
|
|
||||||
return c.createWorld();
|
return c.createWorld();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int countCustomBiomes();
|
int countCustomBiomes();
|
||||||
|
|
||||||
@ -130,13 +128,9 @@ public interface INMSBinding {
|
|||||||
|
|
||||||
KList<String> getStructureKeys();
|
KList<String> getStructureKeys();
|
||||||
|
|
||||||
AutoClosing injectLevelStems();
|
boolean missingDimensionTypes(String... keys);
|
||||||
|
|
||||||
default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
default boolean injectBukkit() {
|
||||||
return null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
|
|
||||||
|
|
||||||
void removeCustomDimensions(World world);
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import java.util.function.Supplier;
|
|||||||
//https://minecraft.wiki/w/Pack_format
|
//https://minecraft.wiki/w/Pack_format
|
||||||
@Getter
|
@Getter
|
||||||
public enum DataVersion {
|
public enum DataVersion {
|
||||||
|
UNSUPPORTED("0.0.0", 0, () -> null),
|
||||||
V1192("1.19.2", 10, DataFixerV1192::new),
|
V1192("1.19.2", 10, DataFixerV1192::new),
|
||||||
V1205("1.20.6", 41, DataFixerV1206::new),
|
V1205("1.20.6", 41, DataFixerV1206::new),
|
||||||
V1213("1.21.3", 57, DataFixerV1213::new);
|
V1213("1.21.3", 57, DataFixerV1213::new);
|
||||||
|
@ -1,28 +1,31 @@
|
|||||||
package com.volmit.iris.core.nms.datapack;
|
package com.volmit.iris.core.nms.datapack;
|
||||||
|
|
||||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||||
import com.volmit.iris.engine.object.IrisRange;
|
import com.volmit.iris.engine.object.IrisDimensionTypeOptions;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public interface IDataFixer {
|
public interface IDataFixer {
|
||||||
|
|
||||||
default JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
|
default JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject rawDimension(Dimension dimension);
|
JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options);
|
||||||
|
|
||||||
default JSONObject createDimension(Dimension dimension, IrisRange height, int logicalHeight) {
|
void fixDimension(Dimension dimension, JSONObject json);
|
||||||
JSONObject obj = rawDimension(dimension);
|
|
||||||
obj.put("min_y", height.getMin());
|
default JSONObject createDimension(Dimension base, int minY, int height, int logicalHeight, @Nullable IrisDimensionTypeOptions options) {
|
||||||
obj.put("height", height.getMax() - height.getMin());
|
JSONObject obj = resolve(base, options);
|
||||||
|
obj.put("min_y", minY);
|
||||||
|
obj.put("height", height);
|
||||||
obj.put("logical_height", logicalHeight);
|
obj.put("logical_height", logicalHeight);
|
||||||
|
fixDimension(base, obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Dimension {
|
enum Dimension {
|
||||||
OVERRWORLD,
|
OVERWORLD,
|
||||||
NETHER,
|
NETHER,
|
||||||
THE_END
|
END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,81 +1,104 @@
|
|||||||
package com.volmit.iris.core.nms.datapack.v1192;
|
package com.volmit.iris.core.nms.datapack.v1192;
|
||||||
|
|
||||||
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
||||||
|
import com.volmit.iris.engine.object.IrisDimensionTypeOptions;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
|
||||||
|
|
||||||
public class DataFixerV1192 implements IDataFixer {
|
public class DataFixerV1192 implements IDataFixer {
|
||||||
|
private static final Map<Dimension, IrisDimensionTypeOptions> OPTIONS = Map.of(
|
||||||
|
Dimension.OVERWORLD, new IrisDimensionTypeOptions(
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
TRUE,
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
1d,
|
||||||
|
0f,
|
||||||
|
null,
|
||||||
|
192,
|
||||||
|
0),
|
||||||
|
Dimension.NETHER, new IrisDimensionTypeOptions(
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
8d,
|
||||||
|
0.1f,
|
||||||
|
18000L,
|
||||||
|
null,
|
||||||
|
15),
|
||||||
|
Dimension.END, new IrisDimensionTypeOptions(
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
FALSE,
|
||||||
|
1d,
|
||||||
|
0f,
|
||||||
|
6000L,
|
||||||
|
null,
|
||||||
|
0)
|
||||||
|
);
|
||||||
|
|
||||||
private static final Map<Dimension, String> DIMENSIONS = Map.of(
|
private static final Map<Dimension, String> DIMENSIONS = Map.of(
|
||||||
Dimension.OVERRWORLD, """
|
Dimension.OVERWORLD, """
|
||||||
{
|
{
|
||||||
"ambient_light": 0.0,
|
|
||||||
"bed_works": true,
|
|
||||||
"coordinate_scale": 1.0,
|
|
||||||
"effects": "minecraft:overworld",
|
"effects": "minecraft:overworld",
|
||||||
"has_ceiling": false,
|
|
||||||
"has_raids": true,
|
|
||||||
"has_skylight": true,
|
|
||||||
"infiniburn": "#minecraft:infiniburn_overworld",
|
"infiniburn": "#minecraft:infiniburn_overworld",
|
||||||
"monster_spawn_block_light_limit": 0,
|
|
||||||
"monster_spawn_light_level": {
|
"monster_spawn_light_level": {
|
||||||
"type": "minecraft:uniform",
|
"type": "minecraft:uniform",
|
||||||
"value": {
|
"value": {
|
||||||
"max_inclusive": 7,
|
"max_inclusive": 7,
|
||||||
"min_inclusive": 0
|
"min_inclusive": 0
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"natural": true,
|
|
||||||
"piglin_safe": false,
|
|
||||||
"respawn_anchor_works": false,
|
|
||||||
"ultrawarm": false
|
|
||||||
}""",
|
}""",
|
||||||
Dimension.NETHER, """
|
Dimension.NETHER, """
|
||||||
{
|
{
|
||||||
"ambient_light": 0.1,
|
|
||||||
"bed_works": false,
|
|
||||||
"coordinate_scale": 8.0,
|
|
||||||
"effects": "minecraft:the_nether",
|
"effects": "minecraft:the_nether",
|
||||||
"fixed_time": 18000,
|
|
||||||
"has_ceiling": true,
|
|
||||||
"has_raids": false,
|
|
||||||
"has_skylight": false,
|
|
||||||
"infiniburn": "#minecraft:infiniburn_nether",
|
"infiniburn": "#minecraft:infiniburn_nether",
|
||||||
"monster_spawn_block_light_limit": 15,
|
|
||||||
"monster_spawn_light_level": 7,
|
"monster_spawn_light_level": 7,
|
||||||
"natural": false,
|
|
||||||
"piglin_safe": true,
|
|
||||||
"respawn_anchor_works": true,
|
|
||||||
"ultrawarm": true
|
|
||||||
}""",
|
}""",
|
||||||
Dimension.THE_END, """
|
Dimension.END, """
|
||||||
{
|
{
|
||||||
"ambient_light": 0.0,
|
|
||||||
"bed_works": false,
|
|
||||||
"coordinate_scale": 1.0,
|
|
||||||
"effects": "minecraft:the_end",
|
"effects": "minecraft:the_end",
|
||||||
"fixed_time": 6000,
|
|
||||||
"has_ceiling": false,
|
|
||||||
"has_raids": true,
|
|
||||||
"has_skylight": false,
|
|
||||||
"infiniburn": "#minecraft:infiniburn_end",
|
"infiniburn": "#minecraft:infiniburn_end",
|
||||||
"monster_spawn_block_light_limit": 0,
|
|
||||||
"monster_spawn_light_level": {
|
"monster_spawn_light_level": {
|
||||||
"type": "minecraft:uniform",
|
"type": "minecraft:uniform",
|
||||||
"value": {
|
"value": {
|
||||||
"max_inclusive": 7,
|
"max_inclusive": 7,
|
||||||
"min_inclusive": 0
|
"min_inclusive": 0
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"natural": false,
|
|
||||||
"piglin_safe": false,
|
|
||||||
"respawn_anchor_works": false,
|
|
||||||
"ultrawarm": false
|
|
||||||
}"""
|
}"""
|
||||||
);
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JSONObject rawDimension(Dimension dimension) {
|
public JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options) {
|
||||||
return new JSONObject(DIMENSIONS.get(dimension));
|
return options == null ? OPTIONS.get(dimension).toJson() : options.resolve(OPTIONS.get(dimension)).toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fixDimension(Dimension dimension, JSONObject json) {
|
||||||
|
var missing = new JSONObject(DIMENSIONS.get(dimension));
|
||||||
|
for (String key : missing.keySet()) {
|
||||||
|
if (json.has(key)) continue;
|
||||||
|
json.put(key, missing.get(key));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,12 @@ public class DataFixerV1206 extends DataFixerV1192 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JSONObject rawDimension(Dimension dimension) {
|
public void fixDimension(Dimension dimension, JSONObject json) {
|
||||||
JSONObject json = super.rawDimension(dimension);
|
super.fixDimension(dimension, json);
|
||||||
if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel))
|
if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel))
|
||||||
return json;
|
return;
|
||||||
var value = (JSONObject) lightLevel.remove("value");
|
var value = (JSONObject) lightLevel.remove("value");
|
||||||
lightLevel.put("max_inclusive", value.get("max_inclusive"));
|
lightLevel.put("max_inclusive", value.get("max_inclusive"));
|
||||||
lightLevel.put("min_inclusive", value.get("min_inclusive"));
|
lightLevel.put("min_inclusive", value.get("min_inclusive"));
|
||||||
return json;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,8 @@ package com.volmit.iris.core.nms.v1X;
|
|||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.Pair;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
@ -121,25 +120,10 @@ public class NMSBinding1X implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AutoClosing injectLevelStems() {
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
return new AutoClosing(() -> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
return injectLevelStems();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeCustomDimensions(World world) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
public CompoundTag serializeEntity(Entity location) {
|
||||||
return null;
|
return null;
|
||||||
@ -224,6 +208,11 @@ public class NMSBinding1X implements INMSBinding {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataVersion getDataVersion() {
|
||||||
|
return DataVersion.UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBiomeId(Biome biome) {
|
public int getBiomeId(Biome biome) {
|
||||||
return biome.ordinal();
|
return biome.ordinal();
|
||||||
|
@ -447,17 +447,17 @@ public class IrisProject {
|
|||||||
KSet<IrisLootTable> loot = new KSet<>();
|
KSet<IrisLootTable> loot = new KSet<>();
|
||||||
KSet<IrisBlockData> blocks = new KSet<>();
|
KSet<IrisBlockData> blocks = new KSet<>();
|
||||||
|
|
||||||
for (String i : dm.getDimensionLoader().getPossibleKeys()) {
|
for (String i : dm.getBlockLoader().getPossibleKeys()) {
|
||||||
blocks.add(dm.getBlockLoader().load(i));
|
blocks.add(dm.getBlockLoader().load(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
dimension.getRegions().forEach((i) -> regions.add(dm.getRegionLoader().load(i)));
|
dimension.getRegions().forEach((i) -> regions.add(dm.getRegionLoader().load(i)));
|
||||||
dimension.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i)));
|
dimension.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i)));
|
||||||
regions.forEach((i) -> biomes.addAll(i.getAllBiomes(null)));
|
regions.forEach((i) -> biomes.addAll(i.getAllBiomes(() -> dm)));
|
||||||
regions.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))));
|
regions.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))));
|
||||||
regions.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
|
regions.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
|
||||||
dimension.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp)));
|
dimension.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp)));
|
||||||
biomes.forEach((i) -> i.getGenerators().forEach((j) -> generators.add(j.getCachedGenerator(null))));
|
biomes.forEach((i) -> i.getGenerators().forEach((j) -> generators.add(j.getCachedGenerator(() -> dm))));
|
||||||
biomes.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))));
|
biomes.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))));
|
||||||
biomes.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
|
biomes.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
|
||||||
spawners.forEach((i) -> i.getSpawns().forEach((j) -> entities.add(dm.getEntityLoader().load(j.getEntity()))));
|
spawners.forEach((i) -> i.getSpawns().forEach((j) -> entities.add(dm.getEntityLoader().load(j.getEntity()))));
|
||||||
|
@ -142,6 +142,8 @@ public class SchemaBuilder {
|
|||||||
|
|
||||||
JSONObject property = buildProperty(k, c);
|
JSONObject property = buildProperty(k, c);
|
||||||
|
|
||||||
|
if (property.getBoolean("!required"))
|
||||||
|
required.put(k.getName());
|
||||||
property.remove("!required");
|
property.remove("!required");
|
||||||
properties.put(k.getName(), property);
|
properties.put(k.getName(), property);
|
||||||
}
|
}
|
||||||
@ -153,19 +155,7 @@ public class SchemaBuilder {
|
|||||||
o.put("properties", properties);
|
o.put("properties", properties);
|
||||||
|
|
||||||
|
|
||||||
if (c.isAnnotationPresent(Snippet.class)) {
|
return buildSnippet(o, c);
|
||||||
JSONObject anyOf = new JSONObject();
|
|
||||||
JSONArray arr = new JSONArray();
|
|
||||||
JSONObject str = new JSONObject();
|
|
||||||
str.put("type", "string");
|
|
||||||
arr.put(o);
|
|
||||||
arr.put(str);
|
|
||||||
anyOf.put("anyOf", arr);
|
|
||||||
|
|
||||||
return anyOf;
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject buildProperty(Field k, Class<?> cl) {
|
private JSONObject buildProperty(Field k, Class<?> cl) {
|
||||||
@ -515,8 +505,16 @@ public class SchemaBuilder {
|
|||||||
boolean present = !typeDesc.isBlank();
|
boolean present = !typeDesc.isBlank();
|
||||||
if (present) d.add(typeDesc);
|
if (present) d.add(typeDesc);
|
||||||
|
|
||||||
if (k.getType().isAnnotationPresent(Snippet.class)) {
|
Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class);
|
||||||
String sm = k.getType().getDeclaredAnnotation(Snippet.class).value();
|
if (snippet == null) {
|
||||||
|
ArrayType array = k.getType().getDeclaredAnnotation(ArrayType.class);
|
||||||
|
if (array != null) {
|
||||||
|
snippet = array.type().getDeclaredAnnotation(Snippet.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snippet != null) {
|
||||||
|
String sm = snippet.value();
|
||||||
if (present) d.add(" ");
|
if (present) d.add(" ");
|
||||||
d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here.");
|
d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here.");
|
||||||
present = false;
|
present = false;
|
||||||
@ -547,42 +545,42 @@ public class SchemaBuilder {
|
|||||||
.replace("</h>", "");
|
.replace("</h>", "");
|
||||||
String hDesc = d.toString("<br>");
|
String hDesc = d.toString("<br>");
|
||||||
prop.put("type", type);
|
prop.put("type", type);
|
||||||
prop.put("description", desc);
|
prop.put("description", d.toString("\n"));
|
||||||
prop.put("x-intellij-html-description", hDesc);
|
prop.put("x-intellij-html-description", hDesc);
|
||||||
|
return buildSnippet(prop, k.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildSnippet(JSONObject prop, Class<?> type) {
|
||||||
|
Snippet snippet = type.getDeclaredAnnotation(Snippet.class);
|
||||||
|
if (snippet == null) return prop;
|
||||||
|
|
||||||
if (k.getType().isAnnotationPresent(Snippet.class)) {
|
|
||||||
JSONObject anyOf = new JSONObject();
|
JSONObject anyOf = new JSONObject();
|
||||||
JSONArray arr = new JSONArray();
|
JSONArray arr = new JSONArray();
|
||||||
JSONObject str = new JSONObject();
|
JSONObject str = new JSONObject();
|
||||||
str.put("type", "string");
|
str.put("type", "string");
|
||||||
String key = "enum-snippet-" + k.getType().getDeclaredAnnotation(Snippet.class).value();
|
String key = "enum-snippet-" + snippet.value();
|
||||||
str.put("$ref", "#/definitions/" + key);
|
str.put("$ref", "#/definitions/" + key);
|
||||||
|
|
||||||
if (!definitions.containsKey(key)) {
|
if (!definitions.containsKey(key)) {
|
||||||
JSONObject j = new JSONObject();
|
JSONObject j = new JSONObject();
|
||||||
JSONArray snl = new JSONArray();
|
JSONArray snl = new JSONArray();
|
||||||
data.getPossibleSnippets(k.getType().getDeclaredAnnotation(Snippet.class).value()).forEach(snl::put);
|
data.getPossibleSnippets(snippet.value()).forEach(snl::put);
|
||||||
j.put("enum", snl);
|
j.put("enum", snl);
|
||||||
definitions.put(key, j);
|
definitions.put(key, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
arr.put(prop);
|
arr.put(prop);
|
||||||
arr.put(str);
|
arr.put(str);
|
||||||
prop.put("description", desc);
|
str.put("description", prop.getString("description"));
|
||||||
prop.put("x-intellij-html-description", hDesc);
|
str.put("x-intellij-html-description", prop.getString("x-intellij-html-description"));
|
||||||
str.put("description", desc);
|
|
||||||
str.put("x-intellij-html-description", hDesc);
|
|
||||||
anyOf.put("anyOf", arr);
|
anyOf.put("anyOf", arr);
|
||||||
anyOf.put("description", desc);
|
anyOf.put("description", prop.getString("description"));
|
||||||
anyOf.put("x-intellij-html-description", hDesc);
|
anyOf.put("x-intellij-html-description", prop.getString("x-intellij-html-description"));
|
||||||
anyOf.put("!required", k.isAnnotationPresent(Required.class));
|
anyOf.put("!required", type.isAnnotationPresent(Required.class));
|
||||||
|
|
||||||
return anyOf;
|
return anyOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
return prop;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String addEnumList(JSONObject prop, KList<String> description, ArrayType t, Object[] values, Function<Object, String> function) {
|
private String addEnumList(JSONObject prop, KList<String> description, ArrayType t, Object[] values, Function<Object, String> function) {
|
||||||
JSONObject items = new JSONObject();
|
JSONObject items = new JSONObject();
|
||||||
|
@ -2,8 +2,13 @@ package com.volmit.iris.core.safeguard;
|
|||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.IrisSettings;
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class IrisSafeguard {
|
public class IrisSafeguard {
|
||||||
|
private static final AtomicBoolean sfg = new AtomicBoolean(false);
|
||||||
public static boolean unstablemode = false;
|
public static boolean unstablemode = false;
|
||||||
public static boolean warningmode = false;
|
public static boolean warningmode = false;
|
||||||
public static boolean stablemode = false;
|
public static boolean stablemode = false;
|
||||||
@ -13,13 +18,15 @@ public class IrisSafeguard {
|
|||||||
ServerBootSFG.BootCheck();
|
ServerBootSFG.BootCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void earlySplash() {
|
public static void splash(boolean early) {
|
||||||
if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode)
|
if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!sfg.getAndSet(true)) {
|
||||||
Iris.instance.splash();
|
Iris.instance.splash();
|
||||||
UtilsSFG.splash();
|
UtilsSFG.splash();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String mode() {
|
public static String mode() {
|
||||||
if (unstablemode) {
|
if (unstablemode) {
|
||||||
@ -30,5 +37,23 @@ public class IrisSafeguard {
|
|||||||
return "stable";
|
return "stable";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static KMap<String, Object> asContext() {
|
||||||
|
KMap<String, Object> m = new KMap<>();
|
||||||
|
m.put("diskSpace", !ServerBootSFG.hasEnoughDiskSpace);
|
||||||
|
m.put("javaVersion", !ServerBootSFG.isCorrectJDK);
|
||||||
|
m.put("jre", ServerBootSFG.isJRE);
|
||||||
|
m.put("missingAgent", ServerBootSFG.missingAgent);
|
||||||
|
m.put("missingDimensionTypes", ServerBootSFG.missingDimensionTypes);
|
||||||
|
m.put("failedInjection", ServerBootSFG.failedInjection);
|
||||||
|
m.put("unsupportedVersion", ServerBootSFG.unsuportedversion);
|
||||||
|
m.put("serverSoftware", !ServerBootSFG.passedserversoftware);
|
||||||
|
KList<String> incompatiblePlugins = new KList<>();
|
||||||
|
ServerBootSFG.incompatibilities.forEach((plugin, present) -> {
|
||||||
|
if (present) incompatiblePlugins.add(plugin);
|
||||||
|
});
|
||||||
|
m.put("plugins", incompatiblePlugins);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package com.volmit.iris.core.safeguard;
|
package com.volmit.iris.core.safeguard;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
|
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
|
||||||
import com.volmit.iris.engine.object.IrisContextInjector;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
|
import com.volmit.iris.util.misc.ServerProperties;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import javax.tools.JavaCompiler;
|
import javax.tools.JavaCompiler;
|
||||||
@ -15,10 +20,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
import static com.volmit.iris.Iris.getJavaVersion;
|
import static com.volmit.iris.Iris.getJavaVersion;
|
||||||
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
|
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
|
||||||
@ -31,6 +33,8 @@ public class ServerBootSFG {
|
|||||||
public static boolean hasPrivileges = true;
|
public static boolean hasPrivileges = true;
|
||||||
public static boolean unsuportedversion = false;
|
public static boolean unsuportedversion = false;
|
||||||
public static boolean missingDimensionTypes = false;
|
public static boolean missingDimensionTypes = false;
|
||||||
|
public static boolean missingAgent = false;
|
||||||
|
public static boolean failedInjection = false;
|
||||||
protected static boolean safeguardPassed;
|
protected static boolean safeguardPassed;
|
||||||
public static boolean passedserversoftware = true;
|
public static boolean passedserversoftware = true;
|
||||||
protected static int count;
|
protected static int count;
|
||||||
@ -45,9 +49,7 @@ public class ServerBootSFG {
|
|||||||
Plugin[] plugins = pluginManager.getPlugins();
|
Plugin[] plugins = pluginManager.getPlugins();
|
||||||
|
|
||||||
incompatibilities.clear();
|
incompatibilities.clear();
|
||||||
incompatibilities.put("Multiverse-Core", false);
|
|
||||||
incompatibilities.put("dynmap", false);
|
incompatibilities.put("dynmap", false);
|
||||||
incompatibilities.put("TerraformGenerator", false);
|
|
||||||
incompatibilities.put("Stratos", false);
|
incompatibilities.put("Stratos", false);
|
||||||
|
|
||||||
String pluginName;
|
String pluginName;
|
||||||
@ -112,11 +114,22 @@ public class ServerBootSFG {
|
|||||||
severityMedium++;
|
severityMedium++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IrisContextInjector.isMissingDimensionTypes()) {
|
if (!Agent.install()) {
|
||||||
|
missingAgent = true;
|
||||||
|
joiner.add("Missing Java Agent");
|
||||||
|
severityHigh++;
|
||||||
|
} else {
|
||||||
|
if (missingDimensionTypes()) {
|
||||||
missingDimensionTypes = true;
|
missingDimensionTypes = true;
|
||||||
joiner.add("Missing Dimension Types");
|
joiner.add("Missing Dimension Types");
|
||||||
severityHigh++;
|
severityHigh++;
|
||||||
}
|
}
|
||||||
|
if (!INMS.get().injectBukkit()) {
|
||||||
|
failedInjection = true;
|
||||||
|
joiner.add("Failed Bukkit Injection");
|
||||||
|
severityHigh++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
allIncompatibilities = joiner.toString();
|
allIncompatibilities = joiner.toString();
|
||||||
|
|
||||||
@ -169,4 +182,32 @@ public class ServerBootSFG {
|
|||||||
return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists());
|
return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean missingDimensionTypes() {
|
||||||
|
return INMS.get().missingDimensionTypes(getDimensionTypes().toArray(String[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KSet<String> getDimensionTypes() {
|
||||||
|
var bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
|
||||||
|
var worlds = bukkit.getConfigurationSection("worlds");
|
||||||
|
if (worlds == null) return new KSet<>();
|
||||||
|
|
||||||
|
var types = new KSet<String>();
|
||||||
|
for (String world : worlds.getKeys(false)) {
|
||||||
|
var gen = worlds.getString(world + ".generator");
|
||||||
|
if (gen == null) continue;
|
||||||
|
|
||||||
|
String loadKey;
|
||||||
|
if (gen.equalsIgnoreCase("iris")) {
|
||||||
|
loadKey = IrisSettings.get().getGenerator().getDefaultWorldType();
|
||||||
|
} else if (gen.startsWith("Iris:")) {
|
||||||
|
loadKey = gen.substring(5);
|
||||||
|
} else continue;
|
||||||
|
|
||||||
|
IrisDimension dimension = Iris.loadDimension(world, loadKey);
|
||||||
|
if (dimension == null) continue;
|
||||||
|
types.add(dimension.getDimensionTypeKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.volmit.iris.core.safeguard;
|
package com.volmit.iris.core.safeguard;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
|
|
||||||
public class UtilsSFG {
|
public class UtilsSFG {
|
||||||
@ -9,7 +10,9 @@ public class UtilsSFG {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void printIncompatibleWarnings() {
|
public static void printIncompatibleWarnings() {
|
||||||
// String SupportedIrisVersion = getDescription().getVersion(); //todo Automatic version
|
String[] parts = Iris.instance.getDescription().getVersion().split("-");
|
||||||
|
String minVersion = parts[1];
|
||||||
|
String maxVersion = parts[2];
|
||||||
|
|
||||||
if (ServerBootSFG.safeguardPassed) {
|
if (ServerBootSFG.safeguardPassed) {
|
||||||
Iris.safeguard(C.BLUE + "0 Conflicts found");
|
Iris.safeguard(C.BLUE + "0 Conflicts found");
|
||||||
@ -21,29 +24,29 @@ public class UtilsSFG {
|
|||||||
Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found");
|
Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ServerBootSFG.incompatibilities.get("Multiverse-Core")) {
|
|
||||||
Iris.safeguard(C.RED + "Multiverse");
|
|
||||||
Iris.safeguard(C.RED + "- The plugin Multiverse is not compatible with the server.");
|
|
||||||
Iris.safeguard(C.RED + "- If you want to have a world manager, consider using PhantomWorlds or MyWorlds instead.");
|
|
||||||
}
|
|
||||||
if (ServerBootSFG.incompatibilities.get("dynmap")) {
|
if (ServerBootSFG.incompatibilities.get("dynmap")) {
|
||||||
Iris.safeguard(C.RED + "Dynmap");
|
Iris.safeguard(C.RED + "Dynmap");
|
||||||
Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server.");
|
Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server.");
|
||||||
Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap.");
|
Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap.");
|
||||||
}
|
}
|
||||||
if (ServerBootSFG.incompatibilities.get("TerraformGenerator") || ServerBootSFG.incompatibilities.get("Stratos")) {
|
if (ServerBootSFG.incompatibilities.get("Stratos")) {
|
||||||
Iris.safeguard(C.YELLOW + "Terraform Generator / Stratos");
|
Iris.safeguard(C.YELLOW + "Stratos");
|
||||||
Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins.");
|
Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins.");
|
||||||
}
|
}
|
||||||
if (ServerBootSFG.unsuportedversion) {
|
if (ServerBootSFG.unsuportedversion) {
|
||||||
Iris.safeguard(C.RED + "Server Version");
|
Iris.safeguard(C.RED + "Server Version");
|
||||||
Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4");
|
Iris.safeguard(C.RED + "- Iris only supports " + minVersion + " > " + maxVersion);
|
||||||
}
|
}
|
||||||
if (ServerBootSFG.missingDimensionTypes) {
|
if (ServerBootSFG.missingDimensionTypes) {
|
||||||
Iris.safeguard(C.RED + "Dimension Types");
|
Iris.safeguard(C.RED + "Dimension Types");
|
||||||
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
|
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
|
||||||
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
|
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
|
||||||
}
|
}
|
||||||
|
if (ServerBootSFG.missingAgent) {
|
||||||
|
Iris.safeguard(C.RED + "Java Agent");
|
||||||
|
Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments.");
|
||||||
|
Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath());
|
||||||
|
}
|
||||||
if (!ServerBootSFG.passedserversoftware) {
|
if (!ServerBootSFG.passedserversoftware) {
|
||||||
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
||||||
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");
|
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");
|
||||||
|
@ -75,6 +75,10 @@ public class ExternalDataSVC implements IrisService {
|
|||||||
if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) {
|
if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) {
|
||||||
Iris.info("EcoItems found, loading EcoItemsDataProvider...");
|
Iris.info("EcoItems found, loading EcoItemsDataProvider...");
|
||||||
}
|
}
|
||||||
|
providers.add(new KGeneratorsDataProvider());
|
||||||
|
if (Bukkit.getPluginManager().getPlugin("KGenerators") != null) {
|
||||||
|
Iris.info("KGenerators found, loading KGeneratorsDataProvider...");
|
||||||
|
}
|
||||||
|
|
||||||
for (ExternalDataProvider p : providers) {
|
for (ExternalDataProvider p : providers) {
|
||||||
if (p.isReady()) {
|
if (p.isReady()) {
|
||||||
|
@ -199,8 +199,10 @@ public class IrisCreator {
|
|||||||
world.get().setTime(6000);
|
world.get().setTime(6000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else
|
} else {
|
||||||
addToBukkitYml();
|
addToBukkitYml();
|
||||||
|
J.s(() -> Iris.linkMultiverseCore.updateWorld(world.get(), dimension));
|
||||||
|
}
|
||||||
|
|
||||||
if (pregen != null) {
|
if (pregen != null) {
|
||||||
CompletableFuture<Boolean> ff = new CompletableFuture<>();
|
CompletableFuture<Boolean> ff = new CompletableFuture<>();
|
||||||
|
@ -108,10 +108,17 @@ public class IrisComplex implements DataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//@builder
|
//@builder
|
||||||
engine.getDimension().getRegions().forEach((i) -> data.getRegionLoader().load(i)
|
if (focusRegion != null) {
|
||||||
.getAllBiomes(this).forEach((b) -> b
|
focusRegion.getAllBiomes(this).forEach(this::registerGenerators);
|
||||||
.getGenerators()
|
} else if (focusBiome != null) {
|
||||||
.forEach((c) -> registerGenerator(c.getCachedGenerator(this)))));
|
registerGenerators(focusBiome);
|
||||||
|
} else {
|
||||||
|
engine.getDimension()
|
||||||
|
.getRegions()
|
||||||
|
.forEach(i -> data.getRegionLoader().load(i)
|
||||||
|
.getAllBiomes(this)
|
||||||
|
.forEach(this::registerGenerators));
|
||||||
|
}
|
||||||
overlayStream = ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream");
|
overlayStream = ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream");
|
||||||
engine.getDimension().getOverlayNoise().forEach(i -> overlayStream = overlayStream.add((x, z) -> i.get(rng, getData(), x, z)));
|
engine.getDimension().getOverlayNoise().forEach(i -> overlayStream = overlayStream.add((x, z) -> i.get(rng, getData(), x, z)));
|
||||||
rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream()
|
rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream()
|
||||||
@ -360,6 +367,10 @@ public class IrisComplex implements DataProvider {
|
|||||||
return Math.max(Math.min(getInterpolatedHeight(engine, x, z, seed) + fluidHeight + overlayStream.get(x, z), engine.getHeight()), 0);
|
return Math.max(Math.min(getInterpolatedHeight(engine, x, z, seed) + fluidHeight + overlayStream.get(x, z), engine.getHeight()), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerGenerators(IrisBiome biome) {
|
||||||
|
biome.getGenerators().forEach(c -> registerGenerator(c.getCachedGenerator(this)));
|
||||||
|
}
|
||||||
|
|
||||||
private void registerGenerator(IrisGenerator cachedGenerator) {
|
private void registerGenerator(IrisGenerator cachedGenerator) {
|
||||||
generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new KSet<>()).add(cachedGenerator);
|
generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new KSet<>()).add(cachedGenerator);
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,14 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData(), true);
|
||||||
|
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), true) : ore;
|
||||||
|
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), true) : ore;
|
||||||
|
if (ore != null) {
|
||||||
|
h.set(xf, i, zf, ore);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (i > he && i <= hf) {
|
if (i > he && i <= hf) {
|
||||||
fdepth = hf - i;
|
fdepth = hf - i;
|
||||||
|
|
||||||
@ -138,9 +146,9 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData());
|
ore = biome.generateOres(realX, i, realZ, rng, getData(), false);
|
||||||
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData()) : ore;
|
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), false) : ore;
|
||||||
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData()) : ore;
|
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), false) : ore;
|
||||||
|
|
||||||
if (ore != null) {
|
if (ore != null) {
|
||||||
h.set(xf, i, zf, ore);
|
h.set(xf, i, zf, ore);
|
||||||
|
@ -52,16 +52,20 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
|
|||||||
BurstExecutor burst = burst().burst(multicore);
|
BurstExecutor burst = burst().burst(multicore);
|
||||||
|
|
||||||
long seed = x * 341873128712L + z * 132897987541L;
|
long seed = x * 341873128712L + z * 132897987541L;
|
||||||
|
long mask = 0;
|
||||||
for (IrisDepositGenerator k : getDimension().getDeposits()) {
|
for (IrisDepositGenerator k : getDimension().getDeposits()) {
|
||||||
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context));
|
long finalSeed = seed * ++mask;
|
||||||
|
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IrisDepositGenerator k : region.getDeposits()) {
|
for (IrisDepositGenerator k : region.getDeposits()) {
|
||||||
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context));
|
long finalSeed = seed * ++mask;
|
||||||
|
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IrisDepositGenerator k : biome.getDeposits()) {
|
for (IrisDepositGenerator k : biome.getDeposits()) {
|
||||||
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context));
|
long finalSeed = seed * ++mask;
|
||||||
|
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
|
||||||
}
|
}
|
||||||
burst.complete();
|
burst.complete();
|
||||||
}
|
}
|
||||||
@ -78,7 +82,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
|
|||||||
if (k.getPerClumpSpawnChance() < rng.d())
|
if (k.getPerClumpSpawnChance() < rng.d())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
IrisObject clump = k.getClump(rng, getData());
|
IrisObject clump = k.getClump(getEngine(), rng, getData());
|
||||||
|
|
||||||
int dim = clump.getW();
|
int dim = clump.getW();
|
||||||
int min = dim / 2;
|
int min = dim / 2;
|
||||||
|
@ -171,12 +171,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
@ArrayType(type = IrisOreGenerator.class, min = 1)
|
@ArrayType(type = IrisOreGenerator.class, min = 1)
|
||||||
private KList<IrisOreGenerator> ores = new KList<>();
|
private KList<IrisOreGenerator> ores = new KList<>();
|
||||||
|
|
||||||
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) {
|
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
|
||||||
if (ores.isEmpty()) {
|
if (ores.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BlockData b = null;
|
BlockData b = null;
|
||||||
for (IrisOreGenerator i : ores) {
|
for (IrisOreGenerator i : ores) {
|
||||||
|
if (i.isGenerateSurface() != surface)
|
||||||
|
continue;
|
||||||
|
|
||||||
b = i.generate(x, y, z, rng, data);
|
b = i.generate(x, y, z, rng, data);
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
|
@ -66,14 +66,12 @@ public class IrisCave extends IrisRegistrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
|
||||||
generate(writer, rng, engine, x, y, z, 0, -1);
|
generate(writer, rng, engine, x, y, z, 0, -1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) {
|
||||||
|
|
||||||
double girth = getWorm().getGirth().get(rng, x, z, engine.getData());
|
double girth = getWorm().getGirth().get(rng, x, z, engine.getData());
|
||||||
KList<IrisPosition> points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, (at) -> {
|
KList<IrisPosition> points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9);
|
||||||
});
|
|
||||||
int highestWater = Math.max(waterHint, -1);
|
int highestWater = Math.max(waterHint, -1);
|
||||||
|
|
||||||
if (highestWater == -1) {
|
if (highestWater == -1) {
|
||||||
|
@ -86,17 +86,13 @@ public class IrisCavePlacer implements IRare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (y == -1) {
|
if (y == -1) {
|
||||||
if(!breakSurface) {
|
int h = (int) caveStartHeight.get(rng, x, z, data);
|
||||||
int eH = engine.getHeight(x, z);
|
int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9);
|
||||||
if (caveStartHeight.getMax() > eH) {
|
y = Math.min(h, ma);
|
||||||
caveStartHeight.setMax(eH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y = (int) caveStartHeight.get(rng, x, z, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint);
|
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail.set(true);
|
fail.set(true);
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
package com.volmit.iris.engine.object;
|
|
||||||
|
|
||||||
import com.volmit.iris.core.nms.INMS;
|
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.misc.ServerProperties;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.NamespacedKey;
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.world.WorldInitEvent;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static com.volmit.iris.Iris.instance;
|
|
||||||
|
|
||||||
public class IrisContextInjector implements Listener {
|
|
||||||
@Getter
|
|
||||||
private static boolean missingDimensionTypes = false;
|
|
||||||
private AutoClosing autoClosing = null;
|
|
||||||
|
|
||||||
public IrisContextInjector() {
|
|
||||||
if (!Bukkit.getWorlds().isEmpty()) return;
|
|
||||||
|
|
||||||
String levelName = ServerProperties.LEVEL_NAME;
|
|
||||||
List<String> irisWorlds = irisWorlds();
|
|
||||||
boolean overworld = irisWorlds.contains(levelName);
|
|
||||||
boolean nether = irisWorlds.contains(levelName + "_nether");
|
|
||||||
boolean end = irisWorlds.contains(levelName + "_end");
|
|
||||||
|
|
||||||
if (INMS.get().missingDimensionTypes(overworld, nether, end)) {
|
|
||||||
missingDimensionTypes = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overworld || nether || end) {
|
|
||||||
autoClosing = INMS.get().injectUncached(overworld, nether, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.registerListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void on(WorldInitEvent event) {
|
|
||||||
if (autoClosing != null) {
|
|
||||||
autoClosing.close();
|
|
||||||
autoClosing = null;
|
|
||||||
}
|
|
||||||
instance.unregisterListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> irisWorlds() {
|
|
||||||
var config = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
|
|
||||||
ConfigurationSection section = config.getConfigurationSection("worlds");
|
|
||||||
if (section == null) return List.of();
|
|
||||||
|
|
||||||
return section.getKeys(false)
|
|
||||||
.stream()
|
|
||||||
.filter(k -> section.getString(k + ".generator", "").startsWith("Iris"))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,6 +20,7 @@ package com.volmit.iris.engine.object;
|
|||||||
|
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.object.annotations.*;
|
import com.volmit.iris.engine.object.annotations.*;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
@ -87,10 +88,10 @@ public class IrisDepositGenerator {
|
|||||||
@Desc("Ore varience is how many different objects clumps iris will create")
|
@Desc("Ore varience is how many different objects clumps iris will create")
|
||||||
private int varience = 3;
|
private int varience = 3;
|
||||||
|
|
||||||
public IrisObject getClump(RNG rng, IrisData rdata) {
|
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) {
|
||||||
KList<IrisObject> objects = this.objects.aquire(() ->
|
KList<IrisObject> objects = this.objects.aquire(() ->
|
||||||
{
|
{
|
||||||
RNG rngv = rng.nextParallelRNG(3957778);
|
RNG rngv = new RNG(engine.getSeedManager().getDeposit() + hashCode());
|
||||||
KList<IrisObject> objectsf = new KList<>();
|
KList<IrisObject> objectsf = new KList<>();
|
||||||
|
|
||||||
for (int i = 0; i < varience; i++) {
|
for (int i = 0; i < varience; i++) {
|
||||||
|
@ -25,6 +25,7 @@ import com.volmit.iris.core.loader.IrisData;
|
|||||||
import com.volmit.iris.core.loader.IrisRegistrant;
|
import com.volmit.iris.core.loader.IrisRegistrant;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
||||||
|
import com.volmit.iris.core.nms.datapack.IDataFixer.Dimension;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.object.annotations.*;
|
import com.volmit.iris.engine.object.annotations.*;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@ -46,8 +47,7 @@ import org.bukkit.Material;
|
|||||||
import org.bukkit.World.Environment;
|
import org.bukkit.World.Environment;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -76,10 +76,6 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
@MaxNumber(2032)
|
@MaxNumber(2032)
|
||||||
@Desc("Maximum height at which players can be teleported to through gameplay.")
|
@Desc("Maximum height at which players can be teleported to through gameplay.")
|
||||||
private int logicalHeight = 256;
|
private int logicalHeight = 256;
|
||||||
@Desc("Maximum height at which players can be teleported to through gameplay.")
|
|
||||||
private int logicalHeightEnd = 256;
|
|
||||||
@Desc("Maximum height at which players can be teleported to through gameplay.")
|
|
||||||
private int logicalHeightNether = 256;
|
|
||||||
@RegistryListResource(IrisJigsawStructure.class)
|
@RegistryListResource(IrisJigsawStructure.class)
|
||||||
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
|
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
|
||||||
private String stronghold;
|
private String stronghold;
|
||||||
@ -168,10 +164,8 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
private int fluidHeight = 63;
|
private int fluidHeight = 63;
|
||||||
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
|
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
|
||||||
private IrisRange dimensionHeight = new IrisRange(-64, 320);
|
private IrisRange dimensionHeight = new IrisRange(-64, 320);
|
||||||
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
|
@Desc("Define options for this dimension")
|
||||||
private IrisRange dimensionHeightEnd = new IrisRange(-64, 320);
|
private IrisDimensionTypeOptions dimensionOptions = new IrisDimensionTypeOptions();
|
||||||
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
|
|
||||||
private IrisRange dimensionHeightNether = new IrisRange(-64, 320);
|
|
||||||
@RegistryListResource(IrisBiome.class)
|
@RegistryListResource(IrisBiome.class)
|
||||||
@Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.")
|
@Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.")
|
||||||
private String focus = "";
|
private String focus = "";
|
||||||
@ -263,12 +257,14 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
return (int) getDimensionHeight().getMin();
|
return (int) getDimensionHeight().getMin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) {
|
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
|
||||||
if (ores.isEmpty()) {
|
if (ores.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BlockData b = null;
|
BlockData b = null;
|
||||||
for (IrisOreGenerator i : ores) {
|
for (IrisOreGenerator i : ores) {
|
||||||
|
if (i.isGenerateSurface() != surface)
|
||||||
|
continue;
|
||||||
|
|
||||||
b = i.generate(x, y, z, rng, data);
|
b = i.generate(x, y, z, rng, data);
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
@ -431,6 +427,39 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dimension getBaseDimension() {
|
||||||
|
return switch (getEnvironment()) {
|
||||||
|
case NETHER -> Dimension.NETHER;
|
||||||
|
case THE_END -> Dimension.END;
|
||||||
|
default -> Dimension.OVERWORLD;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDimensionTypeKey() {
|
||||||
|
return getDimensionType().key();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionType getDimensionType() {
|
||||||
|
return new IrisDimensionType(getBaseDimension(), getDimensionOptions(), getLogicalHeight(), getMaxHeight() - getMinHeight(), getMinHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void installDimensionType(IDataFixer fixer, KList<File> folders) {
|
||||||
|
IrisDimensionType type = getDimensionType();
|
||||||
|
String json = type.toJson(fixer);
|
||||||
|
|
||||||
|
Iris.verbose(" Installing Data Pack Dimension Type: \"iris:" + type.key() + '"');
|
||||||
|
for (File datapacks : folders) {
|
||||||
|
File output = new File(datapacks, "iris/data/iris/dimension_type/" + type.key() + ".json");
|
||||||
|
output.getParentFile().mkdirs();
|
||||||
|
try {
|
||||||
|
IO.writeAll(output, json);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFolderName() {
|
public String getFolderName() {
|
||||||
return "dimensions";
|
return "dimensions";
|
||||||
@ -447,11 +476,12 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void writeShared(KList<File> folders, DimensionHeight height) {
|
public static void writeShared(KList<File> folders, DimensionHeight height) {
|
||||||
Iris.verbose(" Installing Data Pack Dimension Types: \"iris:overworld\", \"iris:the_nether\", \"iris:the_end\"");
|
Iris.verbose(" Installing Data Pack Vanilla Dimension Types");
|
||||||
|
String[] jsonStrings = height.jsonStrings();
|
||||||
for (File datapacks : folders) {
|
for (File datapacks : folders) {
|
||||||
write(datapacks, "overworld", height.overworldType());
|
write(datapacks, "overworld", jsonStrings[0]);
|
||||||
write(datapacks, "the_nether", height.netherType());
|
write(datapacks, "the_nether", jsonStrings[1]);
|
||||||
write(datapacks, "the_end", height.endType());
|
write(datapacks, "the_end", jsonStrings[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
String raw = """
|
String raw = """
|
||||||
@ -476,17 +506,9 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void write(File datapacks, String type, String json) {
|
private static void write(File datapacks, String type, String json) {
|
||||||
File dimType = new File(datapacks, "iris/data/iris/dimension_type/" + type + ".json");
|
if (json == null) return;
|
||||||
File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json");
|
File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json");
|
||||||
|
|
||||||
dimType.getParentFile().mkdirs();
|
|
||||||
try {
|
|
||||||
IO.writeAll(dimType, json);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) {
|
if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) {
|
||||||
dimTypeVanilla.getParentFile().mkdirs();
|
dimTypeVanilla.getParentFile().mkdirs();
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.nms.datapack.IDataFixer;
|
||||||
|
import com.volmit.iris.util.data.Varint;
|
||||||
|
import com.volmit.iris.util.io.IO;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
@Accessors(fluent = true, chain = true)
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public final class IrisDimensionType {
|
||||||
|
@NonNull
|
||||||
|
private final String key;
|
||||||
|
@NonNull
|
||||||
|
private final IDataFixer.Dimension base;
|
||||||
|
@NonNull
|
||||||
|
private final IrisDimensionTypeOptions options;
|
||||||
|
private final int logicalHeight;
|
||||||
|
private final int height;
|
||||||
|
private final int minY;
|
||||||
|
|
||||||
|
public IrisDimensionType(
|
||||||
|
@NonNull IDataFixer.Dimension base,
|
||||||
|
@NonNull IrisDimensionTypeOptions options,
|
||||||
|
int logicalHeight,
|
||||||
|
int height,
|
||||||
|
int minY
|
||||||
|
) {
|
||||||
|
if (logicalHeight > height) throw new IllegalArgumentException("Logical height cannot be greater than height");
|
||||||
|
if (logicalHeight < 0) throw new IllegalArgumentException("Logical height cannot be less than zero");
|
||||||
|
if (height < 16 || height > 4064 ) throw new IllegalArgumentException("Height must be between 16 and 4064");
|
||||||
|
if ((height & 15) != 0) throw new IllegalArgumentException("Height must be a multiple of 16");
|
||||||
|
if (minY < -2032 || minY > 2031) throw new IllegalArgumentException("Min Y must be between -2032 and 2031");
|
||||||
|
if ((minY & 15) != 0) throw new IllegalArgumentException("Min Y must be a multiple of 16");
|
||||||
|
|
||||||
|
this.base = base;
|
||||||
|
this.options = options;
|
||||||
|
this.logicalHeight = logicalHeight;
|
||||||
|
this.height = height;
|
||||||
|
this.minY = minY;
|
||||||
|
this.key = createKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IrisDimensionType fromKey(String key) {
|
||||||
|
var stream = new ByteArrayInputStream(IO.decode(key.replace(".", "=").toUpperCase()));
|
||||||
|
try (var din = new DataInputStream(stream)) {
|
||||||
|
return new IrisDimensionType(
|
||||||
|
IDataFixer.Dimension.values()[din.readUnsignedByte()],
|
||||||
|
new IrisDimensionTypeOptions().read(din),
|
||||||
|
Varint.readUnsignedVarInt(din),
|
||||||
|
Varint.readUnsignedVarInt(din),
|
||||||
|
Varint.readSignedVarInt(din)
|
||||||
|
);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("This is impossible", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toJson(IDataFixer fixer) {
|
||||||
|
return fixer.createDimension(
|
||||||
|
base,
|
||||||
|
minY,
|
||||||
|
height,
|
||||||
|
logicalHeight,
|
||||||
|
options.copy()
|
||||||
|
).toString(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createKey() {
|
||||||
|
var stream = new ByteArrayOutputStream(41);
|
||||||
|
try (var dos = new DataOutputStream(stream)) {
|
||||||
|
dos.writeByte(base.ordinal());
|
||||||
|
options.write(dos);
|
||||||
|
Varint.writeUnsignedVarInt(logicalHeight, dos);
|
||||||
|
Varint.writeUnsignedVarInt(height, dos);
|
||||||
|
Varint.writeSignedVarInt(minY, dos);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("This is impossible", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IO.encode(stream.toByteArray()).replace("=", ".").toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions options() {
|
||||||
|
return options.copy();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,320 @@
|
|||||||
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
|
import com.volmit.iris.engine.object.annotations.Desc;
|
||||||
|
import com.volmit.iris.engine.object.annotations.MaxNumber;
|
||||||
|
import com.volmit.iris.engine.object.annotations.MinNumber;
|
||||||
|
import com.volmit.iris.util.data.Varint;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Accessors(fluent = true, chain = true)
|
||||||
|
@Desc("Optional options for the dimension type")
|
||||||
|
public class IrisDimensionTypeOptions {
|
||||||
|
@MinNumber(0.00001)
|
||||||
|
@MaxNumber(30000000)
|
||||||
|
@Desc("The multiplier applied to coordinates when leaving the dimension. Value between 0.00001 and 30000000.0 (both inclusive).")
|
||||||
|
private double coordinateScale = -1;
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(1)
|
||||||
|
@Desc("How much light the dimension has. When set to 0, it completely follows the light level; when set to 1, there is no ambient lighting.")
|
||||||
|
private float ambientLight = -1;
|
||||||
|
@Nullable
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(Long.MAX_VALUE)
|
||||||
|
@Desc("If this is set to an int, the time of the day is the specified value. To ensure a normal time cycle, set it to null.")
|
||||||
|
private Long fixedTime = -1L;
|
||||||
|
@Nullable
|
||||||
|
@MinNumber(-2032)
|
||||||
|
@MaxNumber(2031)
|
||||||
|
@Desc("Optional value between -2032 and 2031. If set, determines the lower edge of the clouds. If set to null, clouds are disabled in the dimension.")
|
||||||
|
private Integer cloudHeight = -1;
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(15)
|
||||||
|
@Desc("Value between 0 and 15 (both inclusive). Maximum block light required when the monster spawns.")
|
||||||
|
private int monsterSpawnBlockLightLimit = -1;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Desc("Whether the dimensions behaves like the nether (water evaporates and sponges dry) or not. Also lets stalactites drip lava and causes lava to spread faster and thinner.")
|
||||||
|
private TriState ultrawarm = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("When false, compasses spin randomly, and using a bed to set the respawn point or sleep, is disabled. When true, nether portals can spawn zombified piglins, and creaking hearts can spawn creakings.")
|
||||||
|
private TriState natural = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("When false, Piglins and hoglins shake and transform to zombified entities.")
|
||||||
|
private TriState piglinSafe = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("When false, the respawn anchor blows up when trying to set spawn point.")
|
||||||
|
private TriState respawnAnchorWorks = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("When false, the bed blows up when trying to sleep.")
|
||||||
|
private TriState bedWorks = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("Whether players with the Bad Omen effect can cause a raid.")
|
||||||
|
private TriState raids = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("Whether the dimension has skylight or not.")
|
||||||
|
private TriState skylight = DEFAULT;
|
||||||
|
@NonNull
|
||||||
|
@Desc("Whether the dimension has a bedrock ceiling. Note that this is only a logical ceiling. It is unrelated with whether the dimension really has a block ceiling.")
|
||||||
|
private TriState ceiling = DEFAULT;
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions(
|
||||||
|
@NonNull TriState ultrawarm,
|
||||||
|
@NonNull TriState natural,
|
||||||
|
@NonNull TriState piglinSafe,
|
||||||
|
@NonNull TriState respawnAnchorWorks,
|
||||||
|
@NonNull TriState bedWorks,
|
||||||
|
@NonNull TriState raids,
|
||||||
|
@NonNull TriState skylight,
|
||||||
|
@NonNull TriState ceiling,
|
||||||
|
double coordinateScale,
|
||||||
|
float ambientLight,
|
||||||
|
@Nullable Long fixedTime,
|
||||||
|
@Nullable Integer cloudHeight,
|
||||||
|
int monsterSpawnBlockLightLimit
|
||||||
|
) {
|
||||||
|
if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000))
|
||||||
|
throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000");
|
||||||
|
if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1))
|
||||||
|
throw new IllegalArgumentException("Ambient light must be between 0 and 1");
|
||||||
|
if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031))
|
||||||
|
throw new IllegalArgumentException("Cloud height must be between -2032 and 2031");
|
||||||
|
if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15))
|
||||||
|
throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15");
|
||||||
|
|
||||||
|
this.ultrawarm = ultrawarm;
|
||||||
|
this.natural = natural;
|
||||||
|
this.piglinSafe = piglinSafe;
|
||||||
|
this.respawnAnchorWorks = respawnAnchorWorks;
|
||||||
|
this.bedWorks = bedWorks;
|
||||||
|
this.raids = raids;
|
||||||
|
this.skylight = skylight;
|
||||||
|
this.ceiling = ceiling;
|
||||||
|
this.coordinateScale = coordinateScale;
|
||||||
|
this.ambientLight = ambientLight;
|
||||||
|
this.fixedTime = fixedTime;
|
||||||
|
this.cloudHeight = cloudHeight;
|
||||||
|
this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions coordinateScale(double coordinateScale) {
|
||||||
|
if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000))
|
||||||
|
throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000");
|
||||||
|
this.coordinateScale = coordinateScale;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions ambientLight(float ambientLight) {
|
||||||
|
if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1))
|
||||||
|
throw new IllegalArgumentException("Ambient light must be between 0 and 1");
|
||||||
|
this.ambientLight = ambientLight;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions cloudHeight(@Nullable Integer cloudHeight) {
|
||||||
|
if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031))
|
||||||
|
throw new IllegalArgumentException("Cloud height must be between -2032 and 2031");
|
||||||
|
this.cloudHeight = cloudHeight;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions monsterSpawnBlockLightLimit(int monsterSpawnBlockLightLimit) {
|
||||||
|
if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15))
|
||||||
|
throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15");
|
||||||
|
this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(DataOutput dos) throws IOException {
|
||||||
|
int bits = 0;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (TriState state : new TriState[]{
|
||||||
|
ultrawarm,
|
||||||
|
natural,
|
||||||
|
skylight,
|
||||||
|
ceiling,
|
||||||
|
piglinSafe,
|
||||||
|
bedWorks,
|
||||||
|
respawnAnchorWorks,
|
||||||
|
raids
|
||||||
|
}) {
|
||||||
|
if (state == DEFAULT) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bits |= (short) (1 << index++);
|
||||||
|
if (state == TRUE)
|
||||||
|
bits |= (short) (1 << index++);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coordinateScale != -1)
|
||||||
|
bits |= (1 << index++);
|
||||||
|
if (ambientLight != -1)
|
||||||
|
bits |= (1 << index++);
|
||||||
|
if (monsterSpawnBlockLightLimit != -1)
|
||||||
|
bits |= (1 << index++);
|
||||||
|
if (fixedTime != null) {
|
||||||
|
bits |= (1 << index++);
|
||||||
|
if (fixedTime != -1L)
|
||||||
|
bits |= (1 << index++);
|
||||||
|
}
|
||||||
|
if (cloudHeight != null) {
|
||||||
|
bits |= (1 << index++);
|
||||||
|
if (cloudHeight != -1)
|
||||||
|
bits |= (1 << index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Varint.writeSignedVarInt(bits, dos);
|
||||||
|
|
||||||
|
if (coordinateScale != -1)
|
||||||
|
Varint.writeUnsignedVarLong(Double.doubleToLongBits(coordinateScale), dos);
|
||||||
|
if (ambientLight != -1)
|
||||||
|
Varint.writeUnsignedVarInt(Float.floatToIntBits(ambientLight), dos);
|
||||||
|
if (monsterSpawnBlockLightLimit != -1)
|
||||||
|
Varint.writeSignedVarInt(monsterSpawnBlockLightLimit, dos);
|
||||||
|
if (fixedTime != null && fixedTime != -1L)
|
||||||
|
Varint.writeSignedVarLong(fixedTime, dos);
|
||||||
|
if (cloudHeight != null && cloudHeight != -1)
|
||||||
|
Varint.writeSignedVarInt(cloudHeight, dos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions read(DataInput dis) throws IOException {
|
||||||
|
TriState[] states = new TriState[8];
|
||||||
|
Arrays.fill(states, DEFAULT);
|
||||||
|
|
||||||
|
int bits = Varint.readSignedVarInt(dis);
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((bits & (1 << index++)) == 0)
|
||||||
|
continue;
|
||||||
|
states[i] = (bits & (1 << index++)) == 0 ? FALSE : TRUE;
|
||||||
|
}
|
||||||
|
ultrawarm = states[0];
|
||||||
|
natural = states[1];
|
||||||
|
skylight = states[2];
|
||||||
|
ceiling = states[3];
|
||||||
|
piglinSafe = states[4];
|
||||||
|
bedWorks = states[5];
|
||||||
|
respawnAnchorWorks = states[6];
|
||||||
|
raids = states[7];
|
||||||
|
|
||||||
|
coordinateScale = (bits & (1 << index++)) != 0 ? Double.longBitsToDouble(Varint.readUnsignedVarLong(dis)) : -1;
|
||||||
|
ambientLight = (bits & (1 << index++)) != 0 ? Float.intBitsToFloat(Varint.readUnsignedVarInt(dis)) : -1;
|
||||||
|
monsterSpawnBlockLightLimit = (bits & (1 << index++)) != 0 ? Varint.readSignedVarInt(dis) : -1;
|
||||||
|
fixedTime = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarLong(dis) : -1L : null;
|
||||||
|
cloudHeight = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarInt(dis) : -1 : null;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions resolve(IrisDimensionTypeOptions other) {
|
||||||
|
if (ultrawarm == DEFAULT)
|
||||||
|
ultrawarm = other.ultrawarm;
|
||||||
|
if (natural == DEFAULT)
|
||||||
|
natural = other.natural;
|
||||||
|
if (piglinSafe == DEFAULT)
|
||||||
|
piglinSafe = other.piglinSafe;
|
||||||
|
if (respawnAnchorWorks == DEFAULT)
|
||||||
|
respawnAnchorWorks = other.respawnAnchorWorks;
|
||||||
|
if (bedWorks == DEFAULT)
|
||||||
|
bedWorks = other.bedWorks;
|
||||||
|
if (raids == DEFAULT)
|
||||||
|
raids = other.raids;
|
||||||
|
if (skylight == DEFAULT)
|
||||||
|
skylight = other.skylight;
|
||||||
|
if (ceiling == DEFAULT)
|
||||||
|
ceiling = other.ceiling;
|
||||||
|
if (coordinateScale == -1)
|
||||||
|
coordinateScale = other.coordinateScale;
|
||||||
|
if (ambientLight == -1)
|
||||||
|
ambientLight = other.ambientLight;
|
||||||
|
if (fixedTime != null && fixedTime == -1L)
|
||||||
|
fixedTime = other.fixedTime;
|
||||||
|
if (cloudHeight != null && cloudHeight == -1)
|
||||||
|
cloudHeight = other.cloudHeight;
|
||||||
|
if (monsterSpawnBlockLightLimit == -1)
|
||||||
|
monsterSpawnBlockLightLimit = other.monsterSpawnBlockLightLimit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject toJson() {
|
||||||
|
if (!isComplete()) throw new IllegalStateException("Cannot serialize incomplete options");
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("ultrawarm", ultrawarm.bool());
|
||||||
|
json.put("natural", natural.bool());
|
||||||
|
json.put("piglin_safe", piglinSafe.bool());
|
||||||
|
json.put("respawn_anchor_works", respawnAnchorWorks.bool());
|
||||||
|
json.put("bed_works", bedWorks.bool());
|
||||||
|
json.put("has_raids", raids.bool());
|
||||||
|
json.put("has_skylight", skylight.bool());
|
||||||
|
json.put("has_ceiling", ceiling.bool());
|
||||||
|
json.put("coordinate_scale", coordinateScale);
|
||||||
|
json.put("ambient_light", ambientLight);
|
||||||
|
json.put("monster_spawn_block_light_limit", monsterSpawnBlockLightLimit);
|
||||||
|
if (fixedTime != null) json.put("fixed_time", fixedTime);
|
||||||
|
if (cloudHeight != null) json.put("cloud_height", cloudHeight);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisDimensionTypeOptions copy() {
|
||||||
|
return new IrisDimensionTypeOptions(
|
||||||
|
ultrawarm,
|
||||||
|
natural,
|
||||||
|
piglinSafe,
|
||||||
|
respawnAnchorWorks,
|
||||||
|
bedWorks,
|
||||||
|
raids,
|
||||||
|
skylight,
|
||||||
|
ceiling,
|
||||||
|
coordinateScale,
|
||||||
|
ambientLight,
|
||||||
|
fixedTime,
|
||||||
|
cloudHeight,
|
||||||
|
monsterSpawnBlockLightLimit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isComplete() {
|
||||||
|
return ultrawarm != DEFAULT
|
||||||
|
&& natural != DEFAULT
|
||||||
|
&& piglinSafe != DEFAULT
|
||||||
|
&& respawnAnchorWorks != DEFAULT
|
||||||
|
&& bedWorks != DEFAULT
|
||||||
|
&& raids != DEFAULT
|
||||||
|
&& skylight != DEFAULT
|
||||||
|
&& ceiling != DEFAULT
|
||||||
|
&& coordinateScale != -1
|
||||||
|
&& ambientLight != -1
|
||||||
|
&& monsterSpawnBlockLightLimit != -1
|
||||||
|
&& (fixedTime == null || fixedTime != -1L)
|
||||||
|
&& (cloudHeight == null || cloudHeight != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Desc("Allows reusing the behavior of the base dimension")
|
||||||
|
public enum TriState {
|
||||||
|
@Desc("Follow the behavior of the base dimension")
|
||||||
|
DEFAULT,
|
||||||
|
@Desc("True")
|
||||||
|
TRUE,
|
||||||
|
@Desc("False")
|
||||||
|
FALSE;
|
||||||
|
|
||||||
|
public boolean bool() {
|
||||||
|
return this == TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -97,8 +97,7 @@ public class IrisRavine extends IrisRegistrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
|
||||||
KList<IrisPosition> pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, (at) -> {
|
KList<IrisPosition> pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, true, 0);
|
||||||
});
|
|
||||||
CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData());
|
CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData());
|
||||||
CNG bw = baseWidthStyle.getGenerator().createNoCache(rng, engine.getData());
|
CNG bw = baseWidthStyle.getGenerator().createNoCache(rng, engine.getData());
|
||||||
int highestWater = Math.max(waterHint, -1);
|
int highestWater = Math.max(waterHint, -1);
|
||||||
|
@ -151,12 +151,14 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
|||||||
@ArrayType(type = IrisOreGenerator.class, min = 1)
|
@ArrayType(type = IrisOreGenerator.class, min = 1)
|
||||||
private KList<IrisOreGenerator> ores = new KList<>();
|
private KList<IrisOreGenerator> ores = new KList<>();
|
||||||
|
|
||||||
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) {
|
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
|
||||||
if (ores.isEmpty()) {
|
if (ores.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BlockData b = null;
|
BlockData b = null;
|
||||||
for (IrisOreGenerator i : ores) {
|
for (IrisOreGenerator i : ores) {
|
||||||
|
if (i.isGenerateSurface() != surface)
|
||||||
|
continue;
|
||||||
|
|
||||||
b = i.generate(x, y, z, rng, data);
|
b = i.generate(x, y, z, rng, data);
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
|
@ -62,7 +62,7 @@ public class IrisWorm {
|
|||||||
private IrisStyledRange girth = new IrisStyledRange().setMin(3).setMax(5)
|
private IrisStyledRange girth = new IrisStyledRange().setMin(3).setMax(5)
|
||||||
.setStyle(new IrisGeneratorStyle(NoiseStyle.PERLIN));
|
.setStyle(new IrisGeneratorStyle(NoiseStyle.PERLIN));
|
||||||
|
|
||||||
public KList<IrisPosition> generate(RNG rng, IrisData data, MantleWriter writer, IrisRange verticalRange, int x, int y, int z, Consumer<IrisPosition> fork) {
|
public KList<IrisPosition> generate(RNG rng, IrisData data, MantleWriter writer, IrisRange verticalRange, int x, int y, int z, boolean breakSurface, double distance) {
|
||||||
int itr = maxIterations;
|
int itr = maxIterations;
|
||||||
double jx, jy, jz;
|
double jx, jy, jz;
|
||||||
double cx = x;
|
double cx = x;
|
||||||
@ -72,12 +72,11 @@ public class IrisWorm {
|
|||||||
KList<IrisPosition> pos = new KList<>();
|
KList<IrisPosition> pos = new KList<>();
|
||||||
KSet<IrisPosition> check = allowLoops ? null : new KSet<>();
|
KSet<IrisPosition> check = allowLoops ? null : new KSet<>();
|
||||||
CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
|
CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
|
||||||
CNG gy = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
|
CNG gy = yStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
|
||||||
CNG gz = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
|
CNG gz = zStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
|
||||||
|
|
||||||
while (itr-- > 0) {
|
while (itr-- > 0) {
|
||||||
IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
|
IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
|
||||||
fork.accept(current);
|
|
||||||
pos.add(current);
|
pos.add(current);
|
||||||
|
|
||||||
if (check != null) {
|
if (check != null) {
|
||||||
@ -92,6 +91,10 @@ public class IrisWorm {
|
|||||||
cz += jz;
|
cz += jz;
|
||||||
IrisPosition next = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
|
IrisPosition next = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
|
||||||
|
|
||||||
|
if (!breakSurface && writer.getEngineMantle().getHighest(next.getX(), next.getZ(), true) <= next.getY() + distance) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (verticalRange != null && !verticalRange.contains(next.getY())) {
|
if (verticalRange != null && !verticalRange.contains(next.getY())) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,12 @@
|
|||||||
package com.volmit.iris.engine.platform;
|
package com.volmit.iris.engine.platform;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisWorlds;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.service.StudioSVC;
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
import com.volmit.iris.engine.IrisEngine;
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.data.chunk.TerrainChunk;
|
import com.volmit.iris.engine.data.chunk.TerrainChunk;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.framework.EngineTarget;
|
import com.volmit.iris.engine.framework.EngineTarget;
|
||||||
@ -61,7 +62,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@ -87,6 +87,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
private final boolean studio;
|
private final boolean studio;
|
||||||
private final AtomicInteger a = new AtomicInteger(0);
|
private final AtomicInteger a = new AtomicInteger(0);
|
||||||
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
|
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
|
||||||
|
private final AtomicCache<EngineTarget> targetCache = new AtomicCache<>();
|
||||||
private volatile Engine engine;
|
private volatile Engine engine;
|
||||||
private volatile Looper hotloader;
|
private volatile Looper hotloader;
|
||||||
private volatile StudioMode lastMode;
|
private volatile StudioMode lastMode;
|
||||||
@ -113,36 +114,35 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onWorldInit(WorldInitEvent event) {
|
public void onWorldInit(WorldInitEvent event) {
|
||||||
try {
|
|
||||||
if (initialized || !world.name().equals(event.getWorld().getName()))
|
if (initialized || !world.name().equals(event.getWorld().getName()))
|
||||||
return;
|
return;
|
||||||
AutoClosing.closeContext();
|
|
||||||
INMS.get().removeCustomDimensions(event.getWorld());
|
|
||||||
world.setRawWorldSeed(event.getWorld().getSeed());
|
world.setRawWorldSeed(event.getWorld().getSeed());
|
||||||
Engine engine = getEngine(event.getWorld());
|
if (initialize(event.getWorld())) return;
|
||||||
if (engine == null) {
|
|
||||||
Iris.warn("Failed to get Engine!");
|
Iris.warn("Failed to get Engine for " + event.getWorld().getName() + " re-trying...");
|
||||||
J.s(() -> {
|
J.s(() -> {
|
||||||
Engine engine1 = getEngine(event.getWorld());
|
if (!initialize(event.getWorld())) {
|
||||||
if (engine1 != null) {
|
Iris.error("Failed to get Engine for " + event.getWorld().getName() + "!");
|
||||||
try {
|
|
||||||
INMS.get().inject(event.getWorld().getSeed(), engine1, event.getWorld());
|
|
||||||
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
|
|
||||||
initialized = true;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 10);
|
}, 10);
|
||||||
} else {
|
|
||||||
INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld());
|
|
||||||
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
|
|
||||||
spawnChunks.complete(INMS.get().getSpawnChunkCount(event.getWorld()));
|
|
||||||
initialized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean initialize(World world) {
|
||||||
|
Engine engine = getEngine(world);
|
||||||
|
if (engine == null) return false;
|
||||||
|
try {
|
||||||
|
INMS.get().inject(world.getSeed(), engine, world);
|
||||||
|
Iris.info("Injected Iris Biome Source into " + world.getName());
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
Iris.error("Failed to inject biome source into " + world.getName());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
spawnChunks.complete(INMS.get().getSpawnChunkCount(world));
|
||||||
|
Iris.instance.unregisterListener(this);
|
||||||
|
initialized = true;
|
||||||
|
IrisWorlds.get().put(world.getName(), dimensionKey);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -160,6 +160,18 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupEngine() {
|
private void setupEngine() {
|
||||||
|
lastMode = StudioMode.NORMAL;
|
||||||
|
engine = new IrisEngine(getTarget(), studio);
|
||||||
|
populators.clear();
|
||||||
|
targetCache.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public EngineTarget getTarget() {
|
||||||
|
if (engine != null) return engine.getTarget();
|
||||||
|
|
||||||
|
return targetCache.aquire(() -> {
|
||||||
IrisData data = IrisData.get(dataLocation);
|
IrisData data = IrisData.get(dataLocation);
|
||||||
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
|
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
|
||||||
|
|
||||||
@ -188,9 +200,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastMode = StudioMode.NORMAL;
|
return new EngineTarget(world, dimension, data);
|
||||||
engine = new IrisEngine(new EngineTarget(world, dimension, data), studio);
|
});
|
||||||
populators.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,21 +24,23 @@ import com.volmit.iris.engine.framework.EngineTarget;
|
|||||||
import com.volmit.iris.engine.framework.Hotloadable;
|
import com.volmit.iris.engine.framework.Hotloadable;
|
||||||
import com.volmit.iris.util.data.DataProvider;
|
import com.volmit.iris.util.data.DataProvider;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
|
public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
|
||||||
|
@Nullable
|
||||||
Engine getEngine();
|
Engine getEngine();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default IrisData getData() {
|
default IrisData getData() {
|
||||||
return getEngine().getData();
|
return getTarget().getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
default EngineTarget getTarget() {
|
@NotNull
|
||||||
return getEngine().getTarget();
|
EngineTarget getTarget();
|
||||||
}
|
|
||||||
|
|
||||||
void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs);
|
void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs);
|
||||||
|
|
||||||
|
46
core/src/main/java/com/volmit/iris/util/agent/Agent.java
Normal file
46
core/src/main/java/com/volmit/iris/util/agent/Agent.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package com.volmit.iris.util.agent;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||||
|
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
|
||||||
|
public class Agent {
|
||||||
|
private static final String NAME = "com.volmit.iris.util.agent.Installer";
|
||||||
|
public static final File AGENT_JAR = new File(Iris.instance.getDataFolder(), "agent.jar");
|
||||||
|
|
||||||
|
public static ClassReloadingStrategy installed() {
|
||||||
|
return ClassReloadingStrategy.of(getInstrumentation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instrumentation getInstrumentation() {
|
||||||
|
Instrumentation instrumentation = doGetInstrumentation();
|
||||||
|
if (instrumentation == null) throw new IllegalStateException("The agent is not initialized or unavailable");
|
||||||
|
return instrumentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean install() {
|
||||||
|
if (doGetInstrumentation() != null)
|
||||||
|
return true;
|
||||||
|
try {
|
||||||
|
Files.copy(Iris.instance.getResource("agent.jar"), AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
Iris.info("Installing Java Agent...");
|
||||||
|
ByteBuddyAgent.attach(AGENT_JAR, ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return doGetInstrumentation() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Instrumentation doGetInstrumentation() {
|
||||||
|
try {
|
||||||
|
return (Instrumentation) Class.forName(NAME, true, ClassLoader.getSystemClassLoader()).getMethod("getInstrumentation").invoke(null);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -105,7 +105,15 @@ public class B {
|
|||||||
DEEPSLATE_TILES,
|
DEEPSLATE_TILES,
|
||||||
DEEPSLATE_TILE_STAIRS,
|
DEEPSLATE_TILE_STAIRS,
|
||||||
DEEPSLATE_TILE_WALL,
|
DEEPSLATE_TILE_WALL,
|
||||||
CRACKED_DEEPSLATE_TILES
|
CRACKED_DEEPSLATE_TILES,
|
||||||
|
DEEPSLATE_COAL_ORE,
|
||||||
|
DEEPSLATE_IRON_ORE,
|
||||||
|
DEEPSLATE_COPPER_ORE,
|
||||||
|
DEEPSLATE_DIAMOND_ORE,
|
||||||
|
DEEPSLATE_EMERALD_ORE,
|
||||||
|
DEEPSLATE_GOLD_ORE,
|
||||||
|
DEEPSLATE_LAPIS_ORE,
|
||||||
|
DEEPSLATE_REDSTONE_ORE,
|
||||||
}).forEach((i) -> b.add(i.ordinal()));
|
}).forEach((i) -> b.add(i.ordinal()));
|
||||||
|
|
||||||
return IntSets.unmodifiable(b);
|
return IntSets.unmodifiable(b);
|
||||||
|
@ -8,7 +8,7 @@ import com.volmit.iris.util.decree.exceptions.DecreeParsingException;
|
|||||||
public class DataVersionHandler implements DecreeParameterHandler<DataVersion> {
|
public class DataVersionHandler implements DecreeParameterHandler<DataVersion> {
|
||||||
@Override
|
@Override
|
||||||
public KList<DataVersion> getPossibilities() {
|
public KList<DataVersion> getPossibilities() {
|
||||||
return new KList<>(DataVersion.values());
|
return new KList<>(DataVersion.values()).qdel(DataVersion.UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1667,7 +1667,7 @@ public class IO {
|
|||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends OutputStream> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException {
|
public static <T extends Closeable> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException {
|
||||||
File dir = new File(file.getParentFile(), ".tmp");
|
File dir = new File(file.getParentFile(), ".tmp");
|
||||||
dir.mkdirs();
|
dir.mkdirs();
|
||||||
dir.deleteOnExit();
|
dir.deleteOnExit();
|
||||||
|
@ -5,21 +5,7 @@ load: STARTUP
|
|||||||
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
||||||
website: volmit.com
|
website: volmit.com
|
||||||
description: More than a Dimension!
|
description: More than a Dimension!
|
||||||
libraries:
|
libraries: ${libraries}
|
||||||
- com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2
|
|
||||||
- com.github.ben-manes.caffeine:caffeine:3.0.5
|
|
||||||
- org.apache.commons:commons-lang3:3.12.0
|
|
||||||
- commons-io:commons-io:2.13.0
|
|
||||||
- io.timeandspace:smoothie-map:2.0.2
|
|
||||||
- com.google.guava:guava:31.0.1-jre
|
|
||||||
- com.google.code.gson:gson:2.10.1
|
|
||||||
- org.zeroturnaround:zt-zip:1.14
|
|
||||||
- it.unimi.dsi:fastutil:8.5.6
|
|
||||||
- org.ow2.asm:asm:9.2
|
|
||||||
- org.lz4:lz4-java:1.8.0
|
|
||||||
- com.github.oshi:oshi-core:6.6.5
|
|
||||||
- org.dom4j:dom4j:2.1.4
|
|
||||||
- jaxen:jaxen:1.1.6
|
|
||||||
commands:
|
commands:
|
||||||
iris:
|
iris:
|
||||||
aliases: [ ir, irs ]
|
aliases: [ ir, irs ]
|
||||||
|
@ -2,42 +2,45 @@ package com.volmit.iris.core.nms.v1_20_R1;
|
|||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import lombok.SneakyThrows;
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.minecraft.core.*;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.entity.EntityDimensions;
|
import net.minecraft.world.RandomSequences;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
@ -47,15 +50,16 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||||
@ -63,31 +67,26 @@ import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlock;
|
|||||||
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
|
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockStates;
|
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockStates;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
|
|
||||||
@ -95,11 +94,10 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -645,103 +643,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> new ResourceLocation("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().K.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), Lifecycle.stable());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.registryKeySet().forEach(key -> {
|
static void enter(
|
||||||
var value = source.get(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.lifecycle(value);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return new ResourceLocation("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,63 @@
|
|||||||
package com.volmit.iris.core.nms.v1_20_R2;
|
package com.volmit.iris.core.nms.v1_20_R2;
|
||||||
|
|
||||||
import java.awt.Color;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import net.minecraft.core.*;
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -45,58 +67,36 @@ import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
|||||||
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState;
|
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockStates;
|
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockStates;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import java.awt.*;
|
||||||
import com.volmit.iris.Iris;
|
import java.awt.Color;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import java.lang.reflect.Field;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import java.lang.reflect.Method;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import java.lang.reflect.Modifier;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import java.util.*;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import java.util.List;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import java.util.concurrent.Executor;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -624,6 +624,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
|
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> new ResourceLocation("iris", key))
|
||||||
|
.allMatch(type::containsKey);
|
||||||
|
}
|
||||||
|
|
||||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||||
try {
|
try {
|
||||||
for (Field f : clazz.getDeclaredFields()) {
|
for (Field f : clazz.getDeclaredFields()) {
|
||||||
@ -646,103 +654,83 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectLevelStems() {
|
if (injected.getAndSet(true))
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return true;
|
||||||
|
try {
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
Iris.info("Injecting Bukkit");
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var buddy = new ByteBuddy();
|
||||||
var nmsServer = server.getServer();
|
buddy.redefine(ServerLevel.class)
|
||||||
var old = nmsServer.worldLoader;
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
field.setAccessible(true);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
old.resources(),
|
.make()
|
||||||
old.dataConfiguration(),
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
old.datapackWorldgen(),
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
buddy.redefine(clazz)
|
||||||
)));
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
|
||||||
|
.make()
|
||||||
return new AutoClosing(() -> {
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
@SneakyThrows
|
} catch (Throwable e) {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
var reg = registry();
|
e.printStackTrace();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
}
|
||||||
field.setAccessible(true);
|
return false;
|
||||||
|
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
|
||||||
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
return overworld || nether || end;
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
public void removeCustomDimensions(World world) {
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
|
||||||
((CraftWorld) world).getHandle().K.customDimensions = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
|
||||||
var access = registry();
|
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), Lifecycle.stable());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.registryKeySet().forEach(key -> {
|
static void enter(
|
||||||
var value = source.get(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.lifecycle(value);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return new ResourceLocation("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,63 @@
|
|||||||
package com.volmit.iris.core.nms.v1_20_R3;
|
package com.volmit.iris.core.nms.v1_20_R3;
|
||||||
|
|
||||||
import java.awt.Color;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import net.minecraft.core.*;
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -45,58 +67,36 @@ import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
|||||||
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockState;
|
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockState;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockStates;
|
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockStates;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import java.awt.*;
|
||||||
import com.volmit.iris.Iris;
|
import java.awt.Color;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import java.lang.reflect.Field;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import java.lang.reflect.Method;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import java.lang.reflect.Modifier;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import java.util.*;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import java.util.List;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import java.util.concurrent.Executor;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -647,103 +647,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> new ResourceLocation("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().K.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), Lifecycle.stable());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.registryKeySet().forEach(key -> {
|
static void enter(
|
||||||
var value = source.get(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.lifecycle(value);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return new ResourceLocation("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,64 @@
|
|||||||
package com.volmit.iris.core.nms.v1_20_R4;
|
package com.volmit.iris.core.nms.v1_20_R4;
|
||||||
|
|
||||||
import java.awt.Color;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.ByteTag;
|
|
||||||
import net.minecraft.nbt.DoubleTag;
|
|
||||||
import net.minecraft.nbt.EndTag;
|
|
||||||
import net.minecraft.nbt.FloatTag;
|
|
||||||
import net.minecraft.nbt.IntTag;
|
|
||||||
import net.minecraft.nbt.LongTag;
|
|
||||||
import net.minecraft.nbt.ShortTag;
|
|
||||||
import net.minecraft.nbt.StringTag;
|
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.server.commands.data.DataCommands;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -56,55 +67,37 @@ import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
|
|||||||
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockState;
|
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockState;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockStates;
|
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockStates;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockType;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
|
||||||
import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import java.awt.*;
|
||||||
import com.volmit.iris.Iris;
|
import java.awt.Color;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import java.lang.reflect.Field;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import java.lang.reflect.Method;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import java.lang.reflect.Modifier;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import java.util.*;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import java.util.List;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import java.util.concurrent.Executor;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -672,103 +665,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> new ResourceLocation("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().K.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), RegistrationInfo.BUILT_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.registryKeySet().forEach(key -> {
|
static void enter(
|
||||||
var value = source.get(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.registrationInfo(key).orElse(null);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return new ResourceLocation("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,68 @@
|
|||||||
package com.volmit.iris.core.nms.v1_21_R1;
|
package com.volmit.iris.core.nms.v1_21_R1;
|
||||||
|
|
||||||
import java.awt.Color;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import net.minecraft.core.*;
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -51,57 +72,36 @@ import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
|
|||||||
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockState;
|
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockState;
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockStates;
|
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockStates;
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
|
||||||
import org.bukkit.craftbukkit.v1_21_R1.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_21_R1.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import java.awt.*;
|
||||||
import com.volmit.iris.Iris;
|
import java.awt.Color;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import java.lang.reflect.Field;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import java.lang.reflect.Method;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import java.lang.reflect.Modifier;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import java.util.*;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import java.util.List;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import java.util.concurrent.Executor;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -676,103 +676,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().K.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), RegistrationInfo.BUILT_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.registryKeySet().forEach(key -> {
|
static void enter(
|
||||||
var value = source.get(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.registrationInfo(key).orElse(null);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,65 @@
|
|||||||
package com.volmit.iris.core.nms.v1_21_R2;
|
package com.volmit.iris.core.nms.v1_21_R2;
|
||||||
|
|
||||||
import java.awt.Color;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import java.lang.reflect.Field;
|
import com.volmit.iris.Iris;
|
||||||
import java.lang.reflect.Method;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.Pair;
|
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -50,48 +73,32 @@ import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
|
|||||||
import org.bukkit.craftbukkit.v1_21_R2.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_21_R2.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import java.awt.*;
|
||||||
import com.volmit.iris.Iris;
|
import java.awt.Color;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import java.lang.reflect.Field;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import java.lang.reflect.Method;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import java.lang.reflect.Modifier;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import java.util.*;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import java.util.List;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import java.util.concurrent.Executor;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -666,103 +673,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().L.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), RegistrationInfo.BUILT_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.listElementIds().forEach(key -> {
|
static void enter(
|
||||||
var value = source.getValue(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.registrationInfo(key).orElse(null);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package com.volmit.iris.core.nms.v1_21_R3;
|
package com.volmit.iris.core.nms.v1_21_R3;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.Pair;
|
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
@ -22,20 +23,24 @@ import com.volmit.iris.util.nbt.mca.palette.*;
|
|||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
import net.minecraft.core.Registry;
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
@ -47,13 +52,15 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -67,6 +74,7 @@ import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
|
|||||||
import org.bukkit.craftbukkit.v1_21_R3.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_21_R3.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@ -74,23 +82,23 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -665,103 +673,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().L.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), RegistrationInfo.BUILT_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.listElementIds().forEach(key -> {
|
static void enter(
|
||||||
var value = source.getValue(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.registrationInfo(key).orElse(null);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package com.volmit.iris.core.nms.v1_21_R4;
|
package com.volmit.iris.core.nms.v1_21_R4;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
@ -21,20 +22,24 @@ import com.volmit.iris.util.nbt.mca.palette.*;
|
|||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import lombok.SneakyThrows;
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
import net.minecraft.core.Registry;
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
import net.minecraft.core.*;
|
import net.minecraft.core.*;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldLoader;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
@ -46,13 +51,16 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@ -66,6 +74,7 @@ import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
|
|||||||
import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey;
|
import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@ -73,22 +82,23 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -663,103 +673,91 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
public AutoClosing injectLevelStems() {
|
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
.allMatch(type::containsKey);
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
|
||||||
var nmsServer = server.getServer();
|
|
||||||
var old = nmsServer.worldLoader;
|
|
||||||
|
|
||||||
field.setAccessible(true);
|
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
)));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
public boolean injectBukkit() {
|
||||||
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
if (injected.getAndSet(true))
|
||||||
var reg = registry();
|
return true;
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
try {
|
||||||
field.setAccessible(true);
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
buddy.redefine(ServerLevel.class)
|
||||||
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
var fake = new HashMap<>(old);
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
field.set(reg, fake);
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
return new AutoClosing(() -> field.set(reg, old));
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
} catch (Throwable e) {
|
||||||
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
e.printStackTrace();
|
||||||
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
}
|
||||||
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
return false;
|
||||||
return overworld || nether || end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
public void removeCustomDimensions(World world) {
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
((CraftWorld) world).getHandle().L.customDimensions = null;
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
var access = registry();
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
|
||||||
|
|
||||||
var settings = new FlatLevelGeneratorSettings(
|
|
||||||
Optional.empty(),
|
|
||||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
settings.updateLayers();
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
var source = new FlatLevelSource(settings);
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
|
||||||
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
|
||||||
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
|
||||||
if (end) register(fake, dimensions, source, LevelStem.END);
|
|
||||||
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
|
||||||
|
|
||||||
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private static class ChunkAccessAdvice {
|
||||||
var loc = createIrisKey(key);
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
target.register(key, new LevelStem(
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
return index >= access.getPostProcessing().length;
|
||||||
source
|
}
|
||||||
), RegistrationInfo.BUILT_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
private static class ServerLevelAdvice {
|
||||||
if (source == null) return;
|
@Advice.OnMethodEnter
|
||||||
source.listElementIds().forEach(key -> {
|
static void enter(
|
||||||
var value = source.getValue(key);
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
var info = source.registrationInfo(key).orElse(null);
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
if (value != null && info != null && !target.containsKey(key))
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
target.register(key, value, info);
|
@Advice.Argument(12) World.Environment env,
|
||||||
});
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
}
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
try {
|
||||||
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,169 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_21_R5;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisBiome;
|
||||||
|
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.math.RNG;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
|
import net.minecraft.world.level.biome.Climate;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class CustomBiomeSource extends BiomeSource {
|
||||||
|
|
||||||
|
private final long seed;
|
||||||
|
private final Engine engine;
|
||||||
|
private final Registry<Biome> biomeCustomRegistry;
|
||||||
|
private final Registry<Biome> biomeRegistry;
|
||||||
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final RNG rng;
|
||||||
|
private final KMap<String, Holder<Biome>> customBiomes;
|
||||||
|
|
||||||
|
public CustomBiomeSource(long seed, Engine engine, World world) {
|
||||||
|
this.engine = engine;
|
||||||
|
this.seed = seed;
|
||||||
|
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
|
||||||
|
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
|
||||||
|
this.rng = new RNG(engine.getSeedManager().getBiome());
|
||||||
|
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
||||||
|
List<Holder<Biome>> b = new ArrayList<>();
|
||||||
|
|
||||||
|
for (IrisBiome i : engine.getAllBiomes()) {
|
||||||
|
if (i.isCustom()) {
|
||||||
|
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||||
|
b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry
|
||||||
|
.getValue(ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()))).get()).get());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getFor(Class<?> type, Object source) {
|
||||||
|
Object o = fieldFor(type, source);
|
||||||
|
|
||||||
|
if (o != null) {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
return invokeFor(type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object fieldFor(Class<?> returns, Object in) {
|
||||||
|
return fieldForClass(returns, in.getClass(), in);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invokeFor(Class<?> returns, Object in) {
|
||||||
|
for (Method i : in.getClass().getMethods()) {
|
||||||
|
if (i.getReturnType().equals(returns)) {
|
||||||
|
i.setAccessible(true);
|
||||||
|
try {
|
||||||
|
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||||
|
return i.invoke(in);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||||
|
for (Field i : sourceType.getDeclaredFields()) {
|
||||||
|
if (i.getType().equals(returnType)) {
|
||||||
|
i.setAccessible(true);
|
||||||
|
try {
|
||||||
|
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||||
|
return (T) i.get(in);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
||||||
|
return getAllBiomes(
|
||||||
|
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
||||||
|
.lookup(Registries.BIOME).orElse(null),
|
||||||
|
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
|
||||||
|
engine).stream();
|
||||||
|
}
|
||||||
|
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
||||||
|
KMap<String, Holder<Biome>> m = new KMap<>();
|
||||||
|
|
||||||
|
for (IrisBiome i : engine.getAllBiomes()) {
|
||||||
|
if (i.isCustom()) {
|
||||||
|
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||||
|
ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId());
|
||||||
|
Biome biome = customRegistry.getValue(resourceLocation);
|
||||||
|
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
|
||||||
|
if (optionalBiomeKey.isEmpty()) {
|
||||||
|
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
|
||||||
|
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(biomeKey);
|
||||||
|
if (optionalReferenceHolder.isEmpty()) {
|
||||||
|
Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m.put(j.getId(), optionalReferenceHolder.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegistryAccess registry() {
|
||||||
|
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MapCodec<? extends BiomeSource> codec() {
|
||||||
|
throw new UnsupportedOperationException("Not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||||
|
int m = (y - engine.getMinHeight()) << 2;
|
||||||
|
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
||||||
|
if (ib.isCustom()) {
|
||||||
|
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
||||||
|
} else {
|
||||||
|
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
||||||
|
return NMSBinding.biomeToBiomeBase(biomeRegistry, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,311 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_21_R5;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.framework.ResultLocator;
|
||||||
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||||
|
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
|
import com.volmit.iris.util.mantle.MantleFlag;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
|
import net.minecraft.core.*;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.util.random.WeightedList;
|
||||||
|
import net.minecraft.world.entity.MobCategory;
|
||||||
|
import net.minecraft.world.level.*;
|
||||||
|
import net.minecraft.world.level.biome.*;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
|
import net.minecraft.world.level.levelgen.RandomState;
|
||||||
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.generator.CustomChunkGenerator;
|
||||||
|
import org.spigotmc.SpigotWorldConfig;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private final ChunkGenerator delegate;
|
||||||
|
private final Engine engine;
|
||||||
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
|
||||||
|
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||||
|
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.engine = engine;
|
||||||
|
var dimension = engine.getDimension();
|
||||||
|
|
||||||
|
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||||
|
addAll(dimension.getJigsawStructures(), placements);
|
||||||
|
for (var region : dimension.getAllRegions(engine)) {
|
||||||
|
addAll(region.getJigsawStructures(), placements);
|
||||||
|
for (var biome : region.getAllBiomes(engine))
|
||||||
|
addAll(biome.getJigsawStructures(), placements);
|
||||||
|
}
|
||||||
|
var stronghold = dimension.getStronghold();
|
||||||
|
if (stronghold != null)
|
||||||
|
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
||||||
|
placements.removeIf(Objects::isNull);
|
||||||
|
|
||||||
|
var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow();
|
||||||
|
for (var s : placements) {
|
||||||
|
try {
|
||||||
|
String raw = s.getStructureKey();
|
||||||
|
if (raw == null) continue;
|
||||||
|
boolean tag = raw.startsWith("#");
|
||||||
|
if (tag) raw = raw.substring(1);
|
||||||
|
|
||||||
|
var location = ResourceLocation.parse(raw);
|
||||||
|
if (!tag) {
|
||||||
|
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = TagKey.create(Registries.STRUCTURE, location);
|
||||||
|
var set = registry.get(key).orElse(null);
|
||||||
|
if (set == null) {
|
||||||
|
Iris.error("Could not find structure tag: " + raw);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (var holder : set) {
|
||||||
|
var resourceKey = holder.unwrapKey().orElse(null);
|
||||||
|
if (resourceKey == null) continue;
|
||||||
|
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to load structure: " + s.getLoadKey());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
||||||
|
if (placements == null) return;
|
||||||
|
placements.stream()
|
||||||
|
.map(IrisJigsawStructurePlacement::getStructure)
|
||||||
|
.map(engine.getData().getJigsawStructureLoader()::load)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(structures::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
||||||
|
if (holders.size() == 0) return null;
|
||||||
|
if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
|
||||||
|
var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
|
||||||
|
return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
|
||||||
|
}
|
||||||
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
KMap<String, Holder<Structure>> structures = new KMap<>();
|
||||||
|
for (var holder : holders) {
|
||||||
|
if (holder == null) continue;
|
||||||
|
var key = holder.unwrapKey().orElse(null);
|
||||||
|
var set = this.structures.get(key);
|
||||||
|
if (set == null) continue;
|
||||||
|
for (var structure : set) {
|
||||||
|
structures.put(structure, holder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (structures.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var locator = ResultLocator.locateStructure(structures.keySet())
|
||||||
|
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
||||||
|
if (findUnexplored)
|
||||||
|
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
||||||
|
if (result == null) return null;
|
||||||
|
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
||||||
|
return Pair.of(blockPos, result.obj());
|
||||||
|
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MapCodec<? extends ChunkGenerator> codec() {
|
||||||
|
return MapCodec.unit(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkGenerator getDelegate() {
|
||||||
|
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
||||||
|
return chunkGenerator.getDelegate();
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinY() {
|
||||||
|
return delegate.getMinY();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSeaLevel() {
|
||||||
|
return delegate.getSeaLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||||
|
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) {
|
||||||
|
return delegate.createState(holderlookup, randomstate, i, conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||||
|
delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||||
|
return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
||||||
|
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||||
|
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||||
|
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||||
|
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
||||||
|
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
|
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
||||||
|
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
||||||
|
return delegate.getSpawnHeight(levelheightaccessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGenDepth() {
|
||||||
|
return delegate.getGenDepth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
|
||||||
|
return delegate.getTypeNameForDataFixer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() {
|
||||||
|
delegate.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> holder) {
|
||||||
|
return delegate.getBiomeGenerationSettings(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Field biomeSource = null;
|
||||||
|
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
||||||
|
if (!field.getType().equals(BiomeSource.class))
|
||||||
|
continue;
|
||||||
|
biomeSource = field;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (biomeSource == null)
|
||||||
|
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||||
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||||
|
try {
|
||||||
|
BIOME_SOURCE.set(generator, source);
|
||||||
|
if (generator instanceof CustomChunkGenerator custom)
|
||||||
|
BIOME_SOURCE.set(custom.getDelegate(), source);
|
||||||
|
|
||||||
|
return generator;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,762 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_21_R5;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import com.volmit.iris.util.agent.Agent;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
|
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||||
|
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||||
|
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||||
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
|
import com.volmit.iris.util.scheduling.J;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
import net.minecraft.core.*;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.RandomSequences;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.item.component.CustomData;
|
||||||
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
|
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||||
|
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||||
|
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
|
import org.bukkit.*;
|
||||||
|
import org.bukkit.block.Biome;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.CraftChunk;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockState;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockStates;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.block.data.CraftBlockData;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R5.util.CraftNamespacedKey;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
import org.bukkit.generator.BiomeProvider;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class NMSBinding implements INMSBinding {
|
||||||
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
|
private final BlockData AIR = Material.AIR.createBlockData();
|
||||||
|
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||||
|
private final AtomicBoolean injected = new AtomicBoolean();
|
||||||
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
|
private static Object getFor(Class<?> type, Object source) {
|
||||||
|
Object o = fieldFor(type, source);
|
||||||
|
|
||||||
|
if (o != null) {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
return invokeFor(type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object invokeFor(Class<?> returns, Object in) {
|
||||||
|
for (Method i : in.getClass().getMethods()) {
|
||||||
|
if (i.getReturnType().equals(returns)) {
|
||||||
|
i.setAccessible(true);
|
||||||
|
try {
|
||||||
|
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||||
|
return i.invoke(in);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object fieldFor(Class<?> returns, Object in) {
|
||||||
|
return fieldForClass(returns, in.getClass(), in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||||
|
for (Field i : sourceType.getDeclaredFields()) {
|
||||||
|
if (i.getType().equals(returnType)) {
|
||||||
|
i.setAccessible(true);
|
||||||
|
try {
|
||||||
|
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||||
|
return (T) i.get(in);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
||||||
|
return type.getDeclaredClasses()[ordinal];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasTile(Material material) {
|
||||||
|
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasTile(Location l) {
|
||||||
|
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public KMap<String, Object> serializeTile(Location location) {
|
||||||
|
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false);
|
||||||
|
|
||||||
|
if (e == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry());
|
||||||
|
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(value = "null, _, _ -> null", pure = true)
|
||||||
|
private Object convertFromTag(Tag tag, int depth, int maxDepth) {
|
||||||
|
if (tag == null || depth > maxDepth) return null;
|
||||||
|
return switch (tag) {
|
||||||
|
case CollectionTag collection -> {
|
||||||
|
KList<Object> list = new KList<>();
|
||||||
|
|
||||||
|
for (Object i : collection) {
|
||||||
|
if (i instanceof Tag t)
|
||||||
|
list.add(convertFromTag(t, depth + 1, maxDepth));
|
||||||
|
else list.add(i);
|
||||||
|
}
|
||||||
|
yield list;
|
||||||
|
}
|
||||||
|
case net.minecraft.nbt.CompoundTag compound -> {
|
||||||
|
KMap<String, Object> map = new KMap<>();
|
||||||
|
|
||||||
|
for (String key : compound.keySet()) {
|
||||||
|
var child = compound.get(key);
|
||||||
|
if (child == null) continue;
|
||||||
|
var value = convertFromTag(child, depth + 1, maxDepth);
|
||||||
|
if (value == null) continue;
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
yield map;
|
||||||
|
}
|
||||||
|
case NumericTag numeric -> numeric.box();
|
||||||
|
default -> tag.asString().orElse(null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
||||||
|
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
||||||
|
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
||||||
|
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||||
|
J.s(() -> merge(level, blockPos, tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
||||||
|
var blockEntity = level.getBlockEntity(blockPos);
|
||||||
|
if (blockEntity == null) {
|
||||||
|
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||||
|
var state = level.getBlockState(blockPos);
|
||||||
|
if (!state.hasBlockEntity())
|
||||||
|
return;
|
||||||
|
|
||||||
|
blockEntity = ((EntityBlock) state.getBlock())
|
||||||
|
.newBlockEntity(blockPos, state);
|
||||||
|
}
|
||||||
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
||||||
|
return switch (object) {
|
||||||
|
case Map<?, ?> map -> {
|
||||||
|
var tag = new net.minecraft.nbt.CompoundTag();
|
||||||
|
for (var i : map.entrySet()) {
|
||||||
|
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
||||||
|
}
|
||||||
|
yield tag;
|
||||||
|
}
|
||||||
|
case List<?> list -> {
|
||||||
|
var tag = new ListTag();
|
||||||
|
for (var i : list) {
|
||||||
|
tag.add(convertToTag(i, depth + 1, maxDepth));
|
||||||
|
}
|
||||||
|
yield tag;
|
||||||
|
}
|
||||||
|
case Byte number -> ByteTag.valueOf(number);
|
||||||
|
case Short number -> ShortTag.valueOf(number);
|
||||||
|
case Integer number -> IntTag.valueOf(number);
|
||||||
|
case Long number -> LongTag.valueOf(number);
|
||||||
|
case Float number -> FloatTag.valueOf(number);
|
||||||
|
case Double number -> DoubleTag.valueOf(number);
|
||||||
|
case String string -> StringTag.valueOf(string);
|
||||||
|
default -> EndTag.INSTANCE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag serializeEntity(Entity location) {
|
||||||
|
return null;// TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
||||||
|
return null;// TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCustomHeight() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegistryAccess registry() {
|
||||||
|
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
||||||
|
return registry().lookup(Registries.BIOME).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Registry<Block> getBlockRegistry() {
|
||||||
|
return registry().lookup(Registries.BLOCK).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBiomeBaseFromId(int id) {
|
||||||
|
return getCustomBiomeRegistry().get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinHeight(World world) {
|
||||||
|
return world.getMinHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCustomBiomes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTrueBiomeBaseId(Object biomeBase) {
|
||||||
|
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getTrueBiomeBase(Location location) {
|
||||||
|
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTrueBiomeBaseKey(Location location) {
|
||||||
|
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCustomBiomeBaseFor(String mckey) {
|
||||||
|
return getCustomBiomeRegistry().getValue(ResourceLocation.parse(mckey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
||||||
|
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBiomeBaseIdForKey(String key) {
|
||||||
|
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.parse(key)).map(Holder::value).orElse(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyForBiomeBase(Object biomeBase) {
|
||||||
|
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBiomeBase(World world, Biome biome) {
|
||||||
|
return biomeToBiomeBase(((CraftWorld) world).getHandle()
|
||||||
|
.registryAccess().lookup(Registries.BIOME).orElse(null), biome);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBiomeBase(Object registry, Biome biome) {
|
||||||
|
Object v = baseBiomeCache.get(biome);
|
||||||
|
|
||||||
|
if (v != null) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
v = biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
||||||
|
if (v == null) {
|
||||||
|
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
||||||
|
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
||||||
|
// So, we will just return the ID that the plains biome returns instead.
|
||||||
|
//noinspection unchecked
|
||||||
|
return biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
||||||
|
}
|
||||||
|
baseBiomeCache.put(biome, v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KList<Biome> getBiomes() {
|
||||||
|
return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBukkit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBiomeId(Biome biome) {
|
||||||
|
for (World i : Bukkit.getWorlds()) {
|
||||||
|
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
||||||
|
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null);
|
||||||
|
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return biome.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
||||||
|
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
||||||
|
return getCustomBiomeRegistry().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
||||||
|
return getCustomBiomeRegistry().getId(paramT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
||||||
|
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
||||||
|
return new MCABiomeContainer() {
|
||||||
|
@Override
|
||||||
|
public int[] getData() {
|
||||||
|
return base.writeBiomes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBiome(int x, int y, int z, int id) {
|
||||||
|
base.setBiome(x, y, z, biomeMapping.byId(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBiome(int x, int y, int z) {
|
||||||
|
return biomeMapping.getId(base.getBiome(x, y, z));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
||||||
|
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
||||||
|
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
||||||
|
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
||||||
|
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int countCustomBiomes() {
|
||||||
|
AtomicInteger a = new AtomicInteger(0);
|
||||||
|
|
||||||
|
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
||||||
|
if (i.getNamespace().equals("minecraft")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.incrementAndGet();
|
||||||
|
Iris.debug("Custom Biome: " + i);
|
||||||
|
});
|
||||||
|
|
||||||
|
return a.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsDataPacks() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
||||||
|
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
||||||
|
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
||||||
|
c.markUnsaved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
||||||
|
try {
|
||||||
|
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
||||||
|
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
||||||
|
s.setBiome(x, y, z, biome);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Field getFieldForBiomeStorage(Object storage) {
|
||||||
|
Field f = biomeStorageCache;
|
||||||
|
|
||||||
|
if (f != null) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
f = storage.getClass().getDeclaredField("biome");
|
||||||
|
f.setAccessible(true);
|
||||||
|
return f;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.error(storage.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
|
||||||
|
biomeStorageCache = f;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MCAPaletteAccess createPalette() {
|
||||||
|
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
||||||
|
Field cf = IdMapper.class.getDeclaredField("tToId");
|
||||||
|
Field df = IdMapper.class.getDeclaredField("idToT");
|
||||||
|
Field bf = IdMapper.class.getDeclaredField("nextId");
|
||||||
|
cf.setAccessible(true);
|
||||||
|
df.setAccessible(true);
|
||||||
|
bf.setAccessible(true);
|
||||||
|
IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
||||||
|
int b = bf.getInt(blockData);
|
||||||
|
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
||||||
|
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
||||||
|
return new MCAIdMapper<BlockState>(c, d, b);
|
||||||
|
});
|
||||||
|
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
||||||
|
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
||||||
|
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
||||||
|
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||||
|
((CraftBlockData) AIR).getState());
|
||||||
|
return new MCAWrappedPalettedContainer<>(container,
|
||||||
|
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||||
|
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
||||||
|
ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL);
|
||||||
|
AtomicInteger c = new AtomicInteger();
|
||||||
|
AtomicInteger r = new AtomicInteger();
|
||||||
|
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
||||||
|
if (b != null) {
|
||||||
|
if (b.isCustom()) {
|
||||||
|
chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get());
|
||||||
|
c.getAndIncrement();
|
||||||
|
} else {
|
||||||
|
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
||||||
|
r.getAndIncrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
||||||
|
if (customNbt != null && !customNbt.isEmpty()) {
|
||||||
|
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
||||||
|
|
||||||
|
try {
|
||||||
|
net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString());
|
||||||
|
tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe());
|
||||||
|
s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||||
|
} catch (CommandSyntaxException var5) {
|
||||||
|
throw new IllegalArgumentException(var5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CraftItemStack.asBukkitCopy(s);
|
||||||
|
} else {
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||||
|
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||||
|
worldGenContextField.setAccessible(true);
|
||||||
|
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||||
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
|
|
||||||
|
var newContext = new WorldGenContext(
|
||||||
|
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||||
|
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
|
||||||
|
|
||||||
|
worldGenContextField.set(chunkMap, newContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
||||||
|
Field[] fields = EntityType.class.getDeclaredFields();
|
||||||
|
for (Field field : fields) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
||||||
|
try {
|
||||||
|
EntityType entityType = (EntityType) field.get(null);
|
||||||
|
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
||||||
|
Vector<Float> v1 = new Vector<>();
|
||||||
|
v1.add(entityType.getHeight());
|
||||||
|
entityType.getDimensions();
|
||||||
|
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
||||||
|
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
Iris.error("Unable to get entity dimensions!");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
||||||
|
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getBiomeColor(Location location, BiomeColor type) {
|
||||||
|
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||||
|
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||||
|
var biome = holder.value();
|
||||||
|
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
||||||
|
|
||||||
|
int rgba = switch (type) {
|
||||||
|
case FOG -> biome.getFogColor();
|
||||||
|
case WATER -> biome.getWaterColor();
|
||||||
|
case WATER_FOG -> biome.getWaterFogColor();
|
||||||
|
case SKY -> biome.getSkyColor();
|
||||||
|
case FOLIAGE -> biome.getFoliageColor();
|
||||||
|
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
||||||
|
};
|
||||||
|
if (rgba == 0) {
|
||||||
|
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
||||||
|
return null;
|
||||||
|
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Color(rgba, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||||
|
try {
|
||||||
|
for (Field f : clazz.getDeclaredFields()) {
|
||||||
|
if (f.getType().equals(fieldType))
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldType.getName());
|
||||||
|
} catch (NoSuchFieldException var4) {
|
||||||
|
Class<?> superClass = clazz.getSuperclass();
|
||||||
|
if (superClass == null) {
|
||||||
|
throw var4;
|
||||||
|
} else {
|
||||||
|
return getField(superClass, fieldType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
|
||||||
|
return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataVersion getDataVersion() {
|
||||||
|
return DataVersion.V1213;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSpawnChunkCount(World world) {
|
||||||
|
var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS))
|
||||||
|
.orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS));
|
||||||
|
if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!");
|
||||||
|
return (int) Math.pow(2 * radius + 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KList<String> getStructureKeys() {
|
||||||
|
KList<String> keys = new KList<>();
|
||||||
|
|
||||||
|
var registry = registry().lookup(Registries.STRUCTURE).orElse(null);
|
||||||
|
if (registry == null) return keys;
|
||||||
|
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
|
||||||
|
registry.getTags()
|
||||||
|
.map(HolderSet.Named::key)
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.map(s -> "#" + s)
|
||||||
|
.forEach(keys::add);
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(String... keys) {
|
||||||
|
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
return !Arrays.stream(keys)
|
||||||
|
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
|
||||||
|
.allMatch(type::containsKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean injectBukkit() {
|
||||||
|
if (injected.getAndSet(true))
|
||||||
|
return true;
|
||||||
|
try {
|
||||||
|
Iris.info("Injecting Bukkit");
|
||||||
|
var buddy = new ByteBuddy();
|
||||||
|
buddy.redefine(ServerLevel.class)
|
||||||
|
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||||
|
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||||
|
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
|
||||||
|
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||||
|
.make()
|
||||||
|
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||||
|
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||||
|
buddy.redefine(clazz)
|
||||||
|
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
|
||||||
|
.make()
|
||||||
|
.load(clazz.getClassLoader(), Agent.installed());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||||
|
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||||
|
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||||
|
|
||||||
|
var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||||
|
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||||
|
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||||
|
}
|
||||||
|
|
||||||
|
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||||
|
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
|
||||||
|
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||||
|
settings.updateLayers();
|
||||||
|
return new FlatLevelSource(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ChunkAccessAdvice {
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
|
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||||
|
return index >= access.getPostProcessing().length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ServerLevelAdvice {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(
|
||||||
|
@Advice.Argument(0) MinecraftServer server,
|
||||||
|
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||||
|
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||||
|
@Advice.Argument(12) World.Environment env,
|
||||||
|
@Advice.Argument(value = 13) ChunkGenerator gen
|
||||||
|
) {
|
||||||
|
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||||
|
.getClass()
|
||||||
|
.getClassLoader())
|
||||||
|
.getDeclaredMethod("get")
|
||||||
|
.invoke(null);
|
||||||
|
levelStem = (LevelStem) bindings.getClass()
|
||||||
|
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||||
|
.invoke(bindings, server.registryAccess(), gen);
|
||||||
|
|
||||||
|
levelData.customDimensions = null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,21 +15,15 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
pluginManagement {
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plugins {
|
plugins {
|
||||||
id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "Iris"
|
rootProject.name = "Iris"
|
||||||
|
|
||||||
include(":core")
|
include(":core", ":core:agent")
|
||||||
include(
|
include(
|
||||||
|
":nms:v1_21_R5",
|
||||||
":nms:v1_21_R4",
|
":nms:v1_21_R4",
|
||||||
":nms:v1_21_R3",
|
":nms:v1_21_R3",
|
||||||
":nms:v1_21_R2",
|
":nms:v1_21_R2",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user