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:
Julian Krings 2025-07-08 00:36:30 +02:00
commit badf108d56
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
60 changed files with 3554 additions and 1572 deletions

View File

@ -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
View File

@ -0,0 +1,11 @@
plugins {
kotlin("jvm") version "2.0.20"
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.ow2.asm:asm:9.8")
}

View 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()
}
}

View 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
)
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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) { if (dim == null) {
Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); throw new RuntimeException("Can't find dimension " + id + "!");
service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true);
dim = IrisData.loadAnyDimension(id);
if (dim == null) {
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;
}); });
}); });

View File

@ -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;
} }

View 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);
}
}
}

View File

@ -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()) {
.parallel() stream.flatMap(height::merge)
.forEach(dim -> { .parallel()
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); .forEach(dim -> {
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>())); Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
}); 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,19 +133,21 @@ public class ServerConfigurator {
} }
private static void verifyDataPacksPost(boolean allowRestarting) { private static void verifyDataPacksPost(boolean allowRestarting) {
boolean bad = allPacks() try (Stream<IrisData> stream = allPacks()) {
.map(data -> { boolean bad = stream
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath()); .map(data -> {
var loader = data.getDimensionLoader(); Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
return loader.loadAll(loader.getPossibleKeys()) var loader = data.getDimensionLoader();
.stream() return loader.loadAll(loader.getPossibleKeys())
.map(ServerConfigurator::verifyDataPackInstalled) .stream()
.toList() .map(ServerConfigurator::verifyDataPackInstalled)
.contains(false); .toList()
}) .contains(false);
.toList() })
.contains(true); .toList()
if (!bad) return; .contains(true);
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);
} }
} }
} }

View File

@ -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());
}
}

View File

@ -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);
}
@SneakyThrows
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);
} }
getList().remove(world); manager.saveWorldsConfig();
saveConfig();
} }
public void saveConfig() { private WorldManager worldManager() {
try { var api = MultiverseCoreApi.get();
Plugin p = getMultiverse(); return api.getWorldManager();
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) {
worldNameTypes.put(worldName, type);
}
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();
};
} }
} }

View File

@ -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;

View File

@ -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"),

View File

@ -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,13 +90,10 @@ 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()) { return c.createWorld();
ignored.storeContext();
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);
} }

View File

@ -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);

View File

@ -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
} }
} }

View File

@ -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));
}
} }
} }

View File

@ -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;
} }
} }

View File

@ -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();

View File

@ -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()))));

View File

@ -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,40 +545,40 @@ 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());
}
if (k.getType().isAnnotationPresent(Snippet.class)) { private JSONObject buildSnippet(JSONObject prop, Class<?> type) {
JSONObject anyOf = new JSONObject(); Snippet snippet = type.getDeclaredAnnotation(Snippet.class);
JSONArray arr = new JSONArray(); if (snippet == null) return prop;
JSONObject str = new JSONObject();
str.put("type", "string");
String key = "enum-snippet-" + k.getType().getDeclaredAnnotation(Snippet.class).value();
str.put("$ref", "#/definitions/" + key);
if (!definitions.containsKey(key)) { JSONObject anyOf = new JSONObject();
JSONObject j = new JSONObject(); JSONArray arr = new JSONArray();
JSONArray snl = new JSONArray(); JSONObject str = new JSONObject();
data.getPossibleSnippets(k.getType().getDeclaredAnnotation(Snippet.class).value()).forEach(snl::put); str.put("type", "string");
j.put("enum", snl); String key = "enum-snippet-" + snippet.value();
definitions.put(key, j); str.put("$ref", "#/definitions/" + key);
}
arr.put(prop); if (!definitions.containsKey(key)) {
arr.put(str); JSONObject j = new JSONObject();
prop.put("description", desc); JSONArray snl = new JSONArray();
prop.put("x-intellij-html-description", hDesc); data.getPossibleSnippets(snippet.value()).forEach(snl::put);
str.put("description", desc); j.put("enum", snl);
str.put("x-intellij-html-description", hDesc); definitions.put(key, j);
anyOf.put("anyOf", arr);
anyOf.put("description", desc);
anyOf.put("x-intellij-html-description", hDesc);
anyOf.put("!required", k.isAnnotationPresent(Required.class));
return anyOf;
} }
return prop; arr.put(prop);
arr.put(str);
str.put("description", prop.getString("description"));
str.put("x-intellij-html-description", prop.getString("x-intellij-html-description"));
anyOf.put("anyOf", arr);
anyOf.put("description", prop.getString("description"));
anyOf.put("x-intellij-html-description", prop.getString("x-intellij-html-description"));
anyOf.put("!required", type.isAnnotationPresent(Required.class));
return anyOf;
} }
@NotNull @NotNull

View File

@ -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,12 +18,14 @@ 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;
Iris.instance.splash(); if (!sfg.getAndSet(true)) {
UtilsSFG.splash(); Iris.instance.splash();
UtilsSFG.splash();
}
} }
public static String mode() { public static String mode() {
@ -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;
}
} }

View File

@ -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,10 +114,21 @@ public class ServerBootSFG {
severityMedium++; severityMedium++;
} }
if (IrisContextInjector.isMissingDimensionTypes()) { if (!Agent.install()) {
missingDimensionTypes = true; missingAgent = true;
joiner.add("Missing Dimension Types"); joiner.add("Missing Java Agent");
severityHigh++; severityHigh++;
} else {
if (missingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
severityHigh++;
}
if (!INMS.get().injectBukkit()) {
failedInjection = true;
joiner.add("Failed Bukkit Injection");
severityHigh++;
}
} }
allIncompatibilities = joiner.toString(); 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;
}
} }

View File

@ -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.");

View File

@ -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()) {

View File

@ -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<>();

View File

@ -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);
} }

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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++) {

View File

@ -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 {

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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;
} }

View File

@ -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; world.setRawWorldSeed(event.getWorld().getSeed());
AutoClosing.closeContext(); if (initialize(event.getWorld())) return;
INMS.get().removeCustomDimensions(event.getWorld());
world.setRawWorldSeed(event.getWorld().getSeed()); Iris.warn("Failed to get Engine for " + event.getWorld().getName() + " re-trying...");
Engine engine = getEngine(event.getWorld()); J.s(() -> {
if (engine == null) { if (!initialize(event.getWorld())) {
Iris.warn("Failed to get Engine!"); Iris.error("Failed to get Engine for " + event.getWorld().getName() + "!");
J.s(() -> {
Engine engine1 = getEngine(event.getWorld());
if (engine1 != null) {
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);
} 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;
} }
}, 10);
}
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,37 +160,48 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
} }
private void setupEngine() { private void setupEngine() {
IrisData data = IrisData.get(dataLocation); lastMode = StudioMode.NORMAL;
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); engine = new IrisEngine(getTarget(), studio);
populators.clear();
targetCache.reset();
}
if (dimension == null) { @NotNull
Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); @Override
IrisDimension test = IrisData.loadAnyDimension(dimensionKey); public EngineTarget getTarget() {
if (engine != null) return engine.getTarget();
if (test != null) { return targetCache.aquire(() -> {
Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " "); IrisData data = IrisData.get(dataLocation);
Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
Iris.warn("Attempted to install into " + data.getDataFolder().getPath());
data.dump(); if (dimension == null) {
data.clearLists(); Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey);
test = data.getDimensionLoader().load(dimensionKey); IrisDimension test = IrisData.loadAnyDimension(dimensionKey);
if (test != null) { if (test != null) {
Iris.success("Woo! Patched the Engine!"); Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " ");
dimension = test; Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile());
Iris.warn("Attempted to install into " + data.getDataFolder().getPath());
data.dump();
data.clearLists();
test = data.getDimensionLoader().load(dimensionKey);
if (test != null) {
Iris.success("Woo! Patched the Engine!");
dimension = test;
} else {
Iris.error("Failed to patch dimension!");
throw new RuntimeException("Missing Dimension: " + dimensionKey);
}
} else { } else {
Iris.error("Failed to patch dimension!"); Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?");
throw new RuntimeException("Missing Dimension: " + dimensionKey); throw new RuntimeException("Missing Dimension: " + dimensionKey);
} }
} else {
Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?");
throw new RuntimeException("Missing Dimension: " + dimensionKey);
} }
}
lastMode = StudioMode.NORMAL; return new EngineTarget(world, dimension, data);
engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); });
populators.clear();
} }
@Override @Override

View File

@ -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);

View 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;
}
}
}

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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 ]

View File

@ -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();
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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);
}
}
} }
} }

View File

@ -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 {
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var server = ((CraftServer) Bukkit.getServer()); return true;
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); } catch (Throwable e) {
var nmsServer = server.getServer(); Iris.error(C.RED + "Failed to inject Bukkit");
var old = nmsServer.worldLoader; e.printStackTrace();
}
field.setAccessible(true); return false;
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 public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
@SneakyThrows if (!(raw instanceof PlatformChunkGenerator gen))
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { throw new IllegalStateException("Generator is not platform chunk generator!");
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var injected = access.registryOrThrow(Registries.LEVEL_STEM); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); return new LevelStem(dimensionType, chunkGenerator(access));
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
return overworld || nether || end;
}
@Override
public void removeCustomDimensions(World world) {
((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);
}
}
} }
} }

View File

@ -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();
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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);
}
}
} }
} }

View File

@ -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();
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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 }
), 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);
}
}
} }
} }

View File

@ -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();
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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 = ResourceLocation.fromNamespaceAndPath("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 }
), 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);
}
}
} }
} }

View File

@ -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();
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());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.lookupOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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().lookupOrThrow(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 = ResourceLocation.fromNamespaceAndPath("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.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().L.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
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);
}
}
} }
} }

View File

@ -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();
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());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.lookupOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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().lookupOrThrow(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 = ResourceLocation.fromNamespaceAndPath("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.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().L.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
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);
}
}
} }
} }

View File

@ -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();
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());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.lookupOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
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().lookupOrThrow(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 = ResourceLocation.fromNamespaceAndPath("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.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().L.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
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);
}
}
} }
} }

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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",