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"
}
group = "com.volmit"
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
// ======================== WINDOWS =============================
registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins")
@ -63,6 +66,7 @@ val color = "truecolor"
val errorReporting = false
val nmsBindings = mapOf(
"v1_21_R5" to "1.21.7-R0.1-SNAPSHOT",
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
@ -90,6 +94,7 @@ nmsBindings.forEach { key, value ->
dependencies {
compileOnly(project(":core"))
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("net.kyori.ansi.colorLevel", color)
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(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) })
from(project(":core:agent").tasks.jar.flatMap { it.archiveFile })
archiveFileName.set("Iris-${project.version}.jar")
}
@ -168,10 +175,6 @@ fun exec(vararg command: Any) {
p.waitFor()
}
dependencies {
implementation(project(":core"))
}
configurations.configureEach {
resolutionStrategy.cacheChangingModulesFor(60, "minutes")
resolutionStrategy.cacheDynamicVersionsFor(60, "minutes")
@ -193,6 +196,8 @@ allprojects {
maven("https://repo.mineinabyss.com/releases")
maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/")
maven("https://repo.nexomc.com/releases/")
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
maven("https://repo.onarandombox.com/content/groups/public/")
}
dependencies {
@ -275,4 +280,4 @@ fun registerCustomOutputTaskUnix(name: String, path: String) {
into(file(path))
rename { "Iris.jar" }
}
}
}

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 main = "com.volmit.iris.Iris"
repositories {
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
maven("https://repo.auxilor.io/repository/maven-public/")
}
val dynamic: Configuration by configurations.creating
configurations.compileOnly { extendsFrom(dynamic) }
/**
* Dependencies.
@ -48,10 +46,6 @@ dependencies {
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-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
compileOnly("com.nexomc:nexo:1.6.0")
@ -60,9 +54,14 @@ dependencies {
compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8")
compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT")
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")
// Shaded
implementation("com.dfsek:paralithic:0.8.1")
implementation("io.papermc:paperlib:1.0.5")
@ -71,23 +70,21 @@ dependencies {
implementation("net.kyori:adventure-api:4.17.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
compileOnly("io.timeandspace:smoothie-map:2.0.2")
compileOnly("it.unimi.dsi:fastutil:8.5.8")
compileOnly("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2")
compileOnly("org.zeroturnaround:zt-zip:1.14")
compileOnly("com.google.code.gson:gson:2.10.1")
compileOnly("org.ow2.asm:asm:9.2")
compileOnly("com.google.guava:guava:33.0.0-jre")
compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.6")
compileOnly("org.apache.commons:commons-lang3:3.12.0")
compileOnly("com.github.oshi:oshi-core:6.6.5")
compileOnly("org.dom4j:dom4j:2.1.4")
dynamic("commons-io:commons-io:2.13.0")
dynamic("commons-lang:commons-lang:2.6")
dynamic("com.github.oshi:oshi-core:6.6.5")
dynamic("org.lz4:lz4-java:1.8.0")
dynamic("it.unimi.dsi:fastutil:8.5.8")
dynamic("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2")
dynamic("org.zeroturnaround:zt-zip:1.14")
dynamic("com.google.code.gson:gson:2.10.1")
dynamic("org.ow2.asm:asm:9.8")
dynamic("com.github.ben-manes.caffeine:caffeine:3.0.6")
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 {
@ -119,7 +116,8 @@ tasks {
"name" to rootProject.name,
"version" to rootProject.version,
"apiVersion" to apiVersion,
"main" to main
"main" to main,
"libraries" to dynamic.allDependencies.map { "\n - $it" }.sorted().joinToString("")
)
filesMatching("**/plugin.yml") {
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.engine.EnginePanic;
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.IrisWorld;
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.UtilsSFG;
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 io.papermc.lib.PaperLib;
import io.sentry.Sentry;
import lombok.NonNull;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import org.bstats.bukkit.Metrics;
@ -466,15 +465,13 @@ public class Iris extends VolmitPlugin implements Listener {
setupAudience();
setupSentry();
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
INMS.get();
IO.delete(new File("iris"));
compat = IrisCompat.configured(getDataFile("compat.json"));
ServerConfigurator.configure();
new IrisContextInjector();
ExecutionEnvironment.createSimple();
IrisSafeguard.IrisSafeguardSystem();
getSender().setTag(getTag());
IrisSafeguard.earlySplash();
IrisSafeguard.splash(true);
linkMultiverseCore = new MultiverseCoreLink();
linkMythicMobs = new MythicMobsLink();
configWatcher = new FileWatcher(getDataFile("settings.json"));
@ -489,8 +486,7 @@ public class Iris extends VolmitPlugin implements Listener {
J.sr(this::tickQueue, 0);
J.s(this::setupPapi);
J.a(ServerConfigurator::configure, 20);
splash();
UtilsSFG.splash();
IrisSafeguard.splash(false);
autoStartStudio();
checkForBukkitWorlds();
@ -761,37 +757,11 @@ public class Iris extends VolmitPlugin implements Listener {
@Override
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id);
if (worldName.equals("test")) {
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);
}
if (id == null || id.isEmpty()) id = IrisSettings.get().getGenerator().getDefaultWorldType();
Iris.debug("Generator ID: " + id + " requested by bukkit/plugin");
IrisDimension dim = loadDimension(worldName, id);
if (dim == null) {
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true);
dim = IrisData.loadAnyDimension(id);
if (dim == null) {
throw new RuntimeException("Can't find dimension " + id + "!");
} else {
Iris.info("Resolved missing dimension, proceeding with generation.");
}
throw new RuntimeException("Can't find dimension " + id + "!");
}
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());
}
@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() {
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
return;
@ -958,7 +946,7 @@ public class Iris extends VolmitPlugin implements Listener {
private static void setupSentry() {
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("Your server ID is: " + ServerID.ID);
Sentry.init(options -> {
@ -977,7 +965,7 @@ public class Iris extends VolmitPlugin implements Listener {
event.setTag("iris.nms", INMS.get().getClass().getCanonicalName());
var context = IrisContext.get();
if (context != null) event.getContexts().set("engine", context.asContext());
event.getContexts().set("safeguard", ServerBootSFG.allIncompatibilities);
event.getContexts().set("safeguard", IrisSafeguard.asContext());
return event;
});
});

View File

@ -150,7 +150,7 @@ public class IrisSettings {
public boolean useCacheByDefault = true;
public boolean useHighPriority = false;
public boolean useVirtualThreads = false;
public boolean useTicketQueue = false;
public boolean useTicketQueue = true;
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.datapack.DataVersion;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.object.IrisBiome;
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.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
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.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J;
import lombok.Data;
import lombok.NonNull;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
@ -45,12 +42,12 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
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.atomic.AtomicIntegerArray;
import java.util.stream.Stream;
import static com.volmit.iris.core.nms.datapack.IDataFixer.Dimension.*;
public class ServerConfigurator {
public static void configure() {
IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration();
@ -110,19 +107,25 @@ public class ServerConfigurator {
}
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...");
DimensionHeight height = new DimensionHeight(fixer);
KList<File> folders = getDatapacksFolder();
KMap<String, KSet<String>> biomes = new KMap<>();
allPacks().flatMap(height::merge)
.parallel()
.forEach(dim -> {
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
});
try (Stream<IrisData> stream = allPacks()) {
stream.flatMap(height::merge)
.parallel()
.forEach(dim -> {
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);
Iris.info("Data Packs Setup!");
if (fullInstall)
@ -130,19 +133,21 @@ public class ServerConfigurator {
}
private static void verifyDataPacksPost(boolean allowRestarting) {
boolean bad = allPacks()
.map(data -> {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
var loader = data.getDimensionLoader();
return loader.loadAll(loader.getPossibleKeys())
.stream()
.map(ServerConfigurator::verifyDataPackInstalled)
.toList()
.contains(false);
})
.toList()
.contains(true);
if (!bad) return;
try (Stream<IrisData> stream = allPacks()) {
boolean bad = stream
.map(data -> {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
var loader = data.getDimensionLoader();
return loader.loadAll(loader.getPossibleKeys())
.stream()
.map(ServerConfigurator::verifyDataPackInstalled)
.toList()
.contains(false);
})
.toList()
.contains(true);
if (!bad) return;
}
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) {
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!");
@ -225,9 +235,13 @@ public class ServerConfigurator {
}
public static Stream<IrisData> allPacks() {
return Stream.concat(listFiles(new File("plugins/Iris/packs")),
listFiles(Bukkit.getWorldContainer()).map(w -> new File(w, "iris/pack")))
return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")),
IrisWorlds.get().getFolders().map(w -> new File(w, "iris/pack")))
.filter(File::isDirectory)
.filter( base -> {
var content = new File(base, "dimensions").listFiles();
return content != null && content.length > 0;
})
.map(IrisData::get);
}
@ -242,20 +256,24 @@ public class ServerConfigurator {
return path.substring(worldContainer.length(), path.length() - l);
}
@SneakyThrows
private static Stream<File> listFiles(File parent) {
var files = parent.listFiles();
return files == null ? Stream.empty() : Arrays.stream(files);
if (!parent.isDirectory()) return Stream.empty();
return Files.walk(parent.toPath()).map(Path::toFile);
}
@Data
public static class DimensionHeight {
private final IDataFixer fixer;
private IrisRange overworld = new IrisRange();
private IrisRange nether = new IrisRange();
private IrisRange end = new IrisRange();
private int logicalOverworld = 0;
private int logicalNether = 0;
private int logicalEnd = 0;
private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3];
public DimensionHeight(IDataFixer fixer) {
this.fixer = fixer;
for (int i = 0; i < 3; i++) {
dimensions[i] = new AtomicIntegerArray(new int[]{
Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE
});
}
}
public Stream<IrisDimension> merge(IrisData data) {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
@ -266,25 +284,29 @@ public class ServerConfigurator {
}
public void merge(IrisDimension dimension) {
overworld.merge(dimension.getDimensionHeight());
nether.merge(dimension.getDimensionHeight());
end.merge(dimension.getDimensionHeight());
logicalOverworld = Math.max(logicalOverworld, dimension.getLogicalHeight());
logicalNether = Math.max(logicalNether, dimension.getLogicalHeightNether());
logicalEnd = Math.max(logicalEnd, dimension.getLogicalHeightEnd());
AtomicIntegerArray array = dimensions[dimension.getBaseDimension().ordinal()];
array.updateAndGet(0, min -> Math.min(min, dimension.getMinHeight()));
array.updateAndGet(1, max -> Math.max(max, dimension.getMaxHeight()));
array.updateAndGet(2, logical -> Math.max(logical, dimension.getLogicalHeight()));
}
public String overworldType() {
return fixer.createDimension(OVERRWORLD, overworld, logicalOverworld).toString(4);
public String[] jsonStrings() {
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() {
return fixer.createDimension(NETHER, nether, logicalNether).toString(4);
}
public String endType() {
return fixer.createDimension(THE_END, end, logicalEnd).toString(4);
public String jsonString(IDataFixer.Dimension dimension) {
var data = dimensions[dimension.ordinal()];
int minY = data.get(0);
int maxY = data.get(1);
int logicalHeight = data.get(2);
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;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.collection.KMap;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import org.mvplugins.multiverse.core.MultiverseCoreApi;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.options.ImportWorldOptions;
public class MultiverseCoreLink {
private final KMap<String, String> worldNameTypes = new KMap<>();
private final boolean active;
public MultiverseCoreLink() {
}
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;
active = Bukkit.getPluginManager().getPlugin("Multiverse-Core") != null;
}
public void removeFromConfig(World world) {
if (!isSupported()) {
return;
}
getList().remove(world.getName());
saveConfig();
removeFromConfig(world.getName());
}
public void removeFromConfig(String world) {
if (!isSupported()) {
return;
if (!active) 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);
saveConfig();
manager.saveWorldsConfig();
}
public void saveConfig() {
try {
Plugin p = getMultiverse();
Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p);
mvWorldManager.getClass().getDeclaredMethod("saveWorldsConfig").invoke(mvWorldManager);
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
}
public void assignWorldType(String worldName, String type) {
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();
};
private WorldManager worldManager() {
var api = MultiverseCoreApi.get();
return api.getWorldManager();
}
}

View File

@ -44,6 +44,8 @@ import lombok.Data;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.function.Function;
@ -369,6 +371,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
for (ResourceLoader<?> i : loaders.values()) {
i.clearList();
}
possibleSnippets.clear();
}
public String toLoadKey(File f) {
@ -435,8 +438,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
File f = new File(getDataFolder(), r + ".json");
if (f.exists()) {
try {
JsonReader snippetReader = new JsonReader(new FileReader(f));
try (JsonReader snippetReader = new JsonReader(new FileReader(f))){
return adapter.read(snippetReader);
} catch (Throwable e) {
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<>();
File snippetFolder = new File(getDataFolder(), "snippet/" + f);
if (!snippetFolder.exists()) return l;
if (snippetFolder.exists() && snippetFolder.isDirectory()) {
for (File i : snippetFolder.listFiles()) {
l.add("snippet/" + f + "/" + i.getName().split("\\Q.\\E")[0]);
}
String absPath = snippetFolder.getAbsolutePath();
try (var stream = Files.walk(snippetFolder.toPath())) {
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;

View File

@ -35,7 +35,9 @@ public class INMS {
"1.21.2", "v1_21_R2",
"1.21.3", "v1_21_R2",
"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(
new Version(21, 4, "31020"),

View File

@ -18,10 +18,10 @@
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.datapack.DataVersion;
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.KMap;
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.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color;
@ -89,13 +90,10 @@ public interface INMSBinding {
MCABiomeContainer newBiomeContainer(int min, int max);
default World createWorld(WorldCreator c) {
if (missingDimensionTypes(true, true, true))
throw new IllegalStateException("Missing dimenstion types to create world");
try (var ignored = injectLevelStems()) {
ignored.storeContext();
return c.createWorld();
}
if (c.generator() instanceof PlatformChunkGenerator gen
&& missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey()))
throw new IllegalStateException("Missing dimension types to create world");
return c.createWorld();
}
int countCustomBiomes();
@ -130,13 +128,9 @@ public interface INMSBinding {
KList<String> getStructureKeys();
AutoClosing injectLevelStems();
boolean missingDimensionTypes(String... keys);
default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
return null;
default boolean injectBukkit() {
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
@Getter
public enum DataVersion {
UNSUPPORTED("0.0.0", 0, () -> null),
V1192("1.19.2", 10, DataFixerV1192::new),
V1205("1.20.6", 41, DataFixerV1206::new),
V1213("1.21.3", 57, DataFixerV1213::new);

View File

@ -1,28 +1,31 @@
package com.volmit.iris.core.nms.datapack;
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 org.jetbrains.annotations.Nullable;
public interface IDataFixer {
default JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
return json;
}
JSONObject rawDimension(Dimension dimension);
JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options);
default JSONObject createDimension(Dimension dimension, IrisRange height, int logicalHeight) {
JSONObject obj = rawDimension(dimension);
obj.put("min_y", height.getMin());
obj.put("height", height.getMax() - height.getMin());
void fixDimension(Dimension dimension, JSONObject json);
default JSONObject createDimension(Dimension base, int minY, int height, int logicalHeight, @Nullable IrisDimensionTypeOptions options) {
JSONObject obj = resolve(base, options);
obj.put("min_y", minY);
obj.put("height", height);
obj.put("logical_height", logicalHeight);
fixDimension(base, obj);
return obj;
}
enum Dimension {
OVERRWORLD,
OVERWORLD,
NETHER,
THE_END
END
}
}

View File

@ -1,81 +1,104 @@
package com.volmit.iris.core.nms.datapack.v1192;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.object.IrisDimensionTypeOptions;
import com.volmit.iris.util.json.JSONObject;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
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(
Dimension.OVERRWORLD, """
Dimension.OVERWORLD, """
{
"ambient_light": 0.0,
"bed_works": true,
"coordinate_scale": 1.0,
"effects": "minecraft:overworld",
"has_ceiling": false,
"has_raids": true,
"has_skylight": true,
"infiniburn": "#minecraft:infiniburn_overworld",
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": {
"type": "minecraft:uniform",
"value": {
"max_inclusive": 7,
"min_inclusive": 0
}
},
"natural": true,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}
}""",
Dimension.NETHER, """
{
"ambient_light": 0.1,
"bed_works": false,
"coordinate_scale": 8.0,
"effects": "minecraft:the_nether",
"fixed_time": 18000,
"has_ceiling": true,
"has_raids": false,
"has_skylight": false,
"infiniburn": "#minecraft:infiniburn_nether",
"monster_spawn_block_light_limit": 15,
"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",
"fixed_time": 6000,
"has_ceiling": false,
"has_raids": true,
"has_skylight": false,
"infiniburn": "#minecraft:infiniburn_end",
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": {
"type": "minecraft:uniform",
"value": {
"max_inclusive": 7,
"min_inclusive": 0
}
},
"natural": false,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}
}"""
);
@Override
public JSONObject rawDimension(Dimension dimension) {
return new JSONObject(DIMENSIONS.get(dimension));
public JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options) {
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
public JSONObject rawDimension(Dimension dimension) {
JSONObject json = super.rawDimension(dimension);
public void fixDimension(Dimension dimension, JSONObject json) {
super.fixDimension(dimension, json);
if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel))
return json;
return;
var value = (JSONObject) lightLevel.remove("value");
lightLevel.put("max_inclusive", value.get("max_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.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.Pair;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
@ -121,25 +120,10 @@ public class NMSBinding1X implements INMSBinding {
}
@Override
public AutoClosing injectLevelStems() {
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) {
public boolean missingDimensionTypes(String... keys) {
return false;
}
@Override
public void removeCustomDimensions(World world) {
}
@Override
public CompoundTag serializeEntity(Entity location) {
return null;
@ -224,6 +208,11 @@ public class NMSBinding1X implements INMSBinding {
return true;
}
@Override
public DataVersion getDataVersion() {
return DataVersion.UNSUPPORTED;
}
@Override
public int getBiomeId(Biome biome) {
return biome.ordinal();

View File

@ -447,17 +447,17 @@ public class IrisProject {
KSet<IrisLootTable> loot = 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));
}
dimension.getRegions().forEach((i) -> regions.add(dm.getRegionLoader().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.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.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))));
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);
if (property.getBoolean("!required"))
required.put(k.getName());
property.remove("!required");
properties.put(k.getName(), property);
}
@ -153,19 +155,7 @@ public class SchemaBuilder {
o.put("properties", properties);
if (c.isAnnotationPresent(Snippet.class)) {
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;
return buildSnippet(o, c);
}
private JSONObject buildProperty(Field k, Class<?> cl) {
@ -515,8 +505,16 @@ public class SchemaBuilder {
boolean present = !typeDesc.isBlank();
if (present) d.add(typeDesc);
if (k.getType().isAnnotationPresent(Snippet.class)) {
String sm = k.getType().getDeclaredAnnotation(Snippet.class).value();
Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class);
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(" ");
d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here.");
present = false;
@ -547,40 +545,40 @@ public class SchemaBuilder {
.replace("</h>", "");
String hDesc = d.toString("<br>");
prop.put("type", type);
prop.put("description", desc);
prop.put("description", d.toString("\n"));
prop.put("x-intellij-html-description", hDesc);
return buildSnippet(prop, k.getType());
}
if (k.getType().isAnnotationPresent(Snippet.class)) {
JSONObject anyOf = new JSONObject();
JSONArray arr = new JSONArray();
JSONObject str = new JSONObject();
str.put("type", "string");
String key = "enum-snippet-" + k.getType().getDeclaredAnnotation(Snippet.class).value();
str.put("$ref", "#/definitions/" + key);
private JSONObject buildSnippet(JSONObject prop, Class<?> type) {
Snippet snippet = type.getDeclaredAnnotation(Snippet.class);
if (snippet == null) return prop;
if (!definitions.containsKey(key)) {
JSONObject j = new JSONObject();
JSONArray snl = new JSONArray();
data.getPossibleSnippets(k.getType().getDeclaredAnnotation(Snippet.class).value()).forEach(snl::put);
j.put("enum", snl);
definitions.put(key, j);
}
JSONObject anyOf = new JSONObject();
JSONArray arr = new JSONArray();
JSONObject str = new JSONObject();
str.put("type", "string");
String key = "enum-snippet-" + snippet.value();
str.put("$ref", "#/definitions/" + key);
arr.put(prop);
arr.put(str);
prop.put("description", desc);
prop.put("x-intellij-html-description", hDesc);
str.put("description", desc);
str.put("x-intellij-html-description", hDesc);
anyOf.put("anyOf", arr);
anyOf.put("description", desc);
anyOf.put("x-intellij-html-description", hDesc);
anyOf.put("!required", k.isAnnotationPresent(Required.class));
return anyOf;
if (!definitions.containsKey(key)) {
JSONObject j = new JSONObject();
JSONArray snl = new JSONArray();
data.getPossibleSnippets(snippet.value()).forEach(snl::put);
j.put("enum", snl);
definitions.put(key, j);
}
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

View File

@ -2,8 +2,13 @@ package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class IrisSafeguard {
private static final AtomicBoolean sfg = new AtomicBoolean(false);
public static boolean unstablemode = false;
public static boolean warningmode = false;
public static boolean stablemode = false;
@ -13,12 +18,14 @@ public class IrisSafeguard {
ServerBootSFG.BootCheck();
}
public static void earlySplash() {
if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode)
public static void splash(boolean early) {
if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode))
return;
Iris.instance.splash();
UtilsSFG.splash();
if (!sfg.getAndSet(true)) {
Iris.instance.splash();
UtilsSFG.splash();
}
}
public static String mode() {
@ -30,5 +37,23 @@ public class IrisSafeguard {
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;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.INMS;
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.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import javax.tools.JavaCompiler;
@ -15,10 +20,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.*;
import static com.volmit.iris.Iris.getJavaVersion;
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
@ -31,6 +33,8 @@ public class ServerBootSFG {
public static boolean hasPrivileges = true;
public static boolean unsuportedversion = false;
public static boolean missingDimensionTypes = false;
public static boolean missingAgent = false;
public static boolean failedInjection = false;
protected static boolean safeguardPassed;
public static boolean passedserversoftware = true;
protected static int count;
@ -45,9 +49,7 @@ public class ServerBootSFG {
Plugin[] plugins = pluginManager.getPlugins();
incompatibilities.clear();
incompatibilities.put("Multiverse-Core", false);
incompatibilities.put("dynmap", false);
incompatibilities.put("TerraformGenerator", false);
incompatibilities.put("Stratos", false);
String pluginName;
@ -112,10 +114,21 @@ public class ServerBootSFG {
severityMedium++;
}
if (IrisContextInjector.isMissingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
if (!Agent.install()) {
missingAgent = true;
joiner.add("Missing Java Agent");
severityHigh++;
} else {
if (missingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
severityHigh++;
}
if (!INMS.get().injectBukkit()) {
failedInjection = true;
joiner.add("Failed Bukkit Injection");
severityHigh++;
}
}
allIncompatibilities = joiner.toString();
@ -169,4 +182,32 @@ public class ServerBootSFG {
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;
import com.volmit.iris.Iris;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.format.C;
public class UtilsSFG {
@ -9,7 +10,9 @@ public class UtilsSFG {
}
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) {
Iris.safeguard(C.BLUE + "0 Conflicts found");
@ -21,29 +24,29 @@ public class UtilsSFG {
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")) {
Iris.safeguard(C.RED + "Dynmap");
Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server.");
Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap.");
}
if (ServerBootSFG.incompatibilities.get("TerraformGenerator") || ServerBootSFG.incompatibilities.get("Stratos")) {
Iris.safeguard(C.YELLOW + "Terraform Generator / Stratos");
if (ServerBootSFG.incompatibilities.get("Stratos")) {
Iris.safeguard(C.YELLOW + "Stratos");
Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins.");
}
if (ServerBootSFG.unsuportedversion) {
Iris.safeguard(C.RED + "Server Version");
Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4");
Iris.safeguard(C.RED + "- Iris only supports " + minVersion + " > " + maxVersion);
}
if (ServerBootSFG.missingDimensionTypes) {
Iris.safeguard(C.RED + "Dimension Types");
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
}
if (ServerBootSFG.missingAgent) {
Iris.safeguard(C.RED + "Java Agent");
Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments.");
Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath());
}
if (!ServerBootSFG.passedserversoftware) {
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");

View File

@ -75,6 +75,10 @@ public class ExternalDataSVC implements IrisService {
if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) {
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) {
if (p.isReady()) {

View File

@ -199,8 +199,10 @@ public class IrisCreator {
world.get().setTime(6000);
}
});
} else
} else {
addToBukkitYml();
J.s(() -> Iris.linkMultiverseCore.updateWorld(world.get(), dimension));
}
if (pregen != null) {
CompletableFuture<Boolean> ff = new CompletableFuture<>();

View File

@ -108,10 +108,17 @@ public class IrisComplex implements DataProvider {
}
//@builder
engine.getDimension().getRegions().forEach((i) -> data.getRegionLoader().load(i)
.getAllBiomes(this).forEach((b) -> b
.getGenerators()
.forEach((c) -> registerGenerator(c.getCachedGenerator(this)))));
if (focusRegion != null) {
focusRegion.getAllBiomes(this).forEach(this::registerGenerators);
} else if (focusBiome != null) {
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");
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()
@ -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);
}
private void registerGenerators(IrisBiome biome) {
biome.getGenerators().forEach(c -> registerGenerator(c.getCachedGenerator(this)));
}
private void registerGenerator(IrisGenerator 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) {
fdepth = hf - i;
@ -138,9 +146,9 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
continue;
}
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData());
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData()) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData()) : ore;
ore = biome.generateOres(realX, i, realZ, rng, getData(), false);
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), false) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), false) : ore;
if (ore != null) {
h.set(xf, i, zf, ore);

View File

@ -52,16 +52,20 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
BurstExecutor burst = burst().burst(multicore);
long seed = x * 341873128712L + z * 132897987541L;
long mask = 0;
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()) {
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()) {
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();
}
@ -78,7 +82,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
if (k.getPerClumpSpawnChance() < rng.d())
continue;
IrisObject clump = k.getClump(rng, getData());
IrisObject clump = k.getClump(getEngine(), rng, getData());
int dim = clump.getW();
int min = dim / 2;

View File

@ -171,12 +171,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@ArrayType(type = IrisOreGenerator.class, min = 1)
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()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
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) {
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());
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);
if (highestWater == -1) {

View File

@ -86,17 +86,13 @@ public class IrisCavePlacer implements IRare {
}
if (y == -1) {
if(!breakSurface) {
int eH = engine.getHeight(x, z);
if (caveStartHeight.getMax() > eH) {
caveStartHeight.setMax(eH);
}
}
y = (int) caveStartHeight.get(rng, x, z, data);
int h = (int) caveStartHeight.get(rng, x, z, data);
int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9);
y = Math.min(h, ma);
}
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) {
e.printStackTrace();
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.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.collection.KList;
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")
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(() ->
{
RNG rngv = rng.nextParallelRNG(3957778);
RNG rngv = new RNG(engine.getSeedManager().getDeposit() + hashCode());
KList<IrisObject> objectsf = new KList<>();
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.nms.INMS;
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.object.annotations.*;
import com.volmit.iris.util.collection.KList;
@ -46,8 +47,7 @@ import org.bukkit.Material;
import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData;
import java.io.File;
import java.io.IOException;
import java.io.*;
@Accessors(chain = true)
@AllArgsConstructor
@ -76,10 +76,6 @@ public class IrisDimension extends IrisRegistrant {
@MaxNumber(2032)
@Desc("Maximum height at which players can be teleported to through gameplay.")
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)
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
private String stronghold;
@ -168,10 +164,8 @@ public class IrisDimension extends IrisRegistrant {
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.")
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.")
private IrisRange dimensionHeightEnd = 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.")
private IrisRange dimensionHeightNether = new IrisRange(-64, 320);
@Desc("Define options for this dimension")
private IrisDimensionTypeOptions dimensionOptions = new IrisDimensionTypeOptions();
@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.")
private String focus = "";
@ -263,12 +257,14 @@ public class IrisDimension extends IrisRegistrant {
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()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
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
public String getFolderName() {
return "dimensions";
@ -447,11 +476,12 @@ public class IrisDimension extends IrisRegistrant {
}
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) {
write(datapacks, "overworld", height.overworldType());
write(datapacks, "the_nether", height.netherType());
write(datapacks, "the_end", height.endType());
write(datapacks, "overworld", jsonStrings[0]);
write(datapacks, "the_nether", jsonStrings[1]);
write(datapacks, "the_end", jsonStrings[2]);
}
String raw = """
@ -476,17 +506,9 @@ public class IrisDimension extends IrisRegistrant {
}
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");
dimType.getParentFile().mkdirs();
try {
IO.writeAll(dimType, json);
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) {
dimTypeVanilla.getParentFile().mkdirs();
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) {
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 bw = baseWidthStyle.getGenerator().createNoCache(rng, engine.getData());
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)
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()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
if (b != null) {

View File

@ -62,7 +62,7 @@ public class IrisWorm {
private IrisStyledRange girth = new IrisStyledRange().setMin(3).setMax(5)
.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;
double jx, jy, jz;
double cx = x;
@ -72,12 +72,11 @@ public class IrisWorm {
KList<IrisPosition> pos = new KList<>();
KSet<IrisPosition> check = allowLoops ? null : new KSet<>();
CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gy = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gz = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gy = yStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gz = zStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
while (itr-- > 0) {
IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));
fork.accept(current);
pos.add(current);
if (check != null) {
@ -92,6 +91,10 @@ public class IrisWorm {
cz += jz;
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())) {
break;
}

View File

@ -19,11 +19,12 @@
package com.volmit.iris.engine.platform;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.loader.IrisData;
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.engine.IrisEngine;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.data.chunk.TerrainChunk;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget;
@ -61,7 +62,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
@ -87,6 +87,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
private final AtomicCache<EngineTarget> targetCache = new AtomicCache<>();
private volatile Engine engine;
private volatile Looper hotloader;
private volatile StudioMode lastMode;
@ -113,36 +114,35 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldInit(WorldInitEvent event) {
try {
if (initialized || !world.name().equals(event.getWorld().getName()))
return;
AutoClosing.closeContext();
INMS.get().removeCustomDimensions(event.getWorld());
world.setRawWorldSeed(event.getWorld().getSeed());
Engine engine = getEngine(event.getWorld());
if (engine == null) {
Iris.warn("Failed to get Engine!");
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;
if (initialized || !world.name().equals(event.getWorld().getName()))
return;
world.setRawWorldSeed(event.getWorld().getSeed());
if (initialize(event.getWorld())) return;
Iris.warn("Failed to get Engine for " + event.getWorld().getName() + " re-trying...");
J.s(() -> {
if (!initialize(event.getWorld())) {
Iris.error("Failed to get Engine for " + event.getWorld().getName() + "!");
}
}, 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) {
Iris.reportError(e);
Iris.error("Failed to inject biome source into " + world.getName());
e.printStackTrace();
}
spawnChunks.complete(INMS.get().getSpawnChunkCount(world));
Iris.instance.unregisterListener(this);
initialized = true;
IrisWorlds.get().put(world.getName(), dimensionKey);
return true;
}
@Nullable
@ -160,37 +160,48 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
private void setupEngine() {
IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
lastMode = StudioMode.NORMAL;
engine = new IrisEngine(getTarget(), studio);
populators.clear();
targetCache.reset();
}
if (dimension == null) {
Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey);
IrisDimension test = IrisData.loadAnyDimension(dimensionKey);
@NotNull
@Override
public EngineTarget getTarget() {
if (engine != null) return engine.getTarget();
if (test != null) {
Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " ");
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);
return targetCache.aquire(() -> {
IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) {
Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey);
IrisDimension test = IrisData.loadAnyDimension(dimensionKey);
if (test != null) {
Iris.success("Woo! Patched the Engine!");
dimension = test;
Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " ");
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 {
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);
}
} else {
Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?");
throw new RuntimeException("Missing Dimension: " + dimensionKey);
}
}
lastMode = StudioMode.NORMAL;
engine = new IrisEngine(new EngineTarget(world, dimension, data), studio);
populators.clear();
return new EngineTarget(world, dimension, data);
});
}
@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.util.data.DataProvider;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
@Nullable
Engine getEngine();
@Override
default IrisData getData() {
return getEngine().getData();
return getTarget().getData();
}
default EngineTarget getTarget() {
return getEngine().getTarget();
}
@NotNull
EngineTarget getTarget();
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_TILE_STAIRS,
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()));
return IntSets.unmodifiable(b);

View File

@ -8,7 +8,7 @@ import com.volmit.iris.util.decree.exceptions.DecreeParsingException;
public class DataVersionHandler implements DecreeParameterHandler<DataVersion> {
@Override
public KList<DataVersion> getPossibilities() {
return new KList<>(DataVersion.values());
return new KList<>(DataVersion.values()).qdel(DataVersion.UNSUPPORTED);
}
@Override

View File

@ -1667,7 +1667,7 @@ public class IO {
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");
dir.mkdirs();
dir.deleteOnExit();

View File

@ -5,21 +5,7 @@ load: STARTUP
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
website: volmit.com
description: More than a Dimension!
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
libraries: ${libraries}
commands:
iris:
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.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
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.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.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 com.volmit.iris.util.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import lombok.SneakyThrows;
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.RegistryAccess;
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.WorldLoader;
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.entity.EntityDimensions;
import net.minecraft.world.RandomSequences;
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.block.Block;
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.ChunkStatus;
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.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_20_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
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.CraftBlockStates;
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.entity.Dolphin;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.entity.EntityType;
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 sun.misc.Unsafe;
import java.awt.*;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public class NMSBinding implements INMSBinding {
@ -95,11 +94,10 @@ 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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -645,103 +643,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
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);
}
@Override
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
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;
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
if (!(raw instanceof PlatformChunkGenerator gen))
throw new IllegalStateException("Generator is not platform chunk generator!");
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return new LevelStem(dimensionType, chunkGenerator(access));
}
@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()
);
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), Lifecycle.stable());
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.registryKeySet().forEach(key -> {
var value = source.get(key);
var info = source.lifecycle(value);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return new ResourceLocation("iris", key.location().getPath());
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

@ -1,41 +1,63 @@
package com.volmit.iris.core.nms.v1_20_R2;
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.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.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
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 lombok.SneakyThrows;
import net.minecraft.core.*;
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.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
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.WorldLoader;
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.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.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.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;
@ -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.CraftBlockStates;
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.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
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 com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
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.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;
import java.awt.*;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -624,6 +624,14 @@ public class NMSBinding implements INMSBinding {
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 {
try {
for (Field f : clazz.getDeclaredFields()) {
@ -646,103 +654,83 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var server = ((CraftServer) Bukkit.getServer());
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();
});
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
if (!(raw instanceof PlatformChunkGenerator gen))
throw new IllegalStateException("Generator is not platform chunk generator!");
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return new LevelStem(dimensionType, chunkGenerator(access));
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
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()
);
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), Lifecycle.stable());
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.registryKeySet().forEach(key -> {
var value = source.get(key);
var info = source.lifecycle(value);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return new ResourceLocation("iris", key.location().getPath());
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

@ -1,41 +1,63 @@
package com.volmit.iris.core.nms.v1_20_R3;
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.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.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
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 lombok.SneakyThrows;
import net.minecraft.core.*;
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.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
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.WorldLoader;
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.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.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.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;
@ -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.CraftBlockStates;
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.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
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 com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
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.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;
import java.awt.*;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -647,103 +647,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
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);
}
@Override
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
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;
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
if (!(raw instanceof PlatformChunkGenerator gen))
throw new IllegalStateException("Generator is not platform chunk generator!");
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return new LevelStem(dimensionType, chunkGenerator(access));
}
@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()
);
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), Lifecycle.stable());
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.registryKeySet().forEach(key -> {
var value = source.get(key);
var info = source.lifecycle(value);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return new ResourceLocation("iris", key.location().getPath());
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

@ -1,53 +1,64 @@
package com.volmit.iris.core.nms.v1_20_R4;
import java.awt.Color;
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.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.core.nms.container.AutoClosing;
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 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.Registry;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
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.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
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.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.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.dimension.DimensionType;
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;
@ -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.block.CraftBlockState;
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.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.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
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 com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
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 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;
import java.awt.*;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -672,103 +665,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
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);
}
@Override
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
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;
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
if (!(raw instanceof PlatformChunkGenerator gen))
throw new IllegalStateException("Generator is not platform chunk generator!");
var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return new LevelStem(dimensionType, chunkGenerator(access));
}
@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()
);
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), RegistrationInfo.BUILT_IN);
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.registryKeySet().forEach(key -> {
var value = source.get(key);
var info = source.registrationInfo(key).orElse(null);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return new ResourceLocation("iris", key.location().getPath());
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

@ -1,47 +1,68 @@
package com.volmit.iris.core.nms.v1_21_R1;
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.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.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.core.nms.container.AutoClosing;
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 lombok.SneakyThrows;
import net.minecraft.core.*;
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.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
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.WorldLoader;
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.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.DimensionType;
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;
@ -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.CraftBlockStates;
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.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
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 com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
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.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;
import java.awt.*;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -676,103 +676,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
public boolean missingDimensionTypes(String... keys) {
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
return !Arrays.stream(keys)
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
.allMatch(type::containsKey);
}
@Override
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
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;
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));
}
@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()
);
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), RegistrationInfo.BUILT_IN);
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.registryKeySet().forEach(key -> {
var value = source.get(key);
var info = source.registrationInfo(key).orElse(null);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
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

@ -1,42 +1,65 @@
package com.volmit.iris.core.nms.v1_21_R2;
import java.awt.Color;
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.serialization.Lifecycle;
import com.volmit.iris.core.nms.container.AutoClosing;
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.container.Pair;
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 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.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.WorldLoader;
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.DimensionType;
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;
@ -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.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 com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
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 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;
import java.awt.*;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -666,103 +673,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
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
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
var registry = registry().lookupOrThrow(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;
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));
}
@Override
public void removeCustomDimensions(World world) {
((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()
);
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();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), RegistrationInfo.BUILT_IN);
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.listElementIds().forEach(key -> {
var value = source.getValue(key);
var info = source.registrationInfo(key).orElse(null);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
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

@ -1,17 +1,18 @@
package com.volmit.iris.core.nms.v1_21_R3;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
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.Pair;
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.collection.KSet;
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;
@ -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.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import lombok.SneakyThrows;
import net.minecraft.core.Registry;
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.Tag;
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.WorldLoader;
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;
@ -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.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.DimensionType;
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;
@ -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.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;
@ -74,23 +82,23 @@ 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.List;
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.locks.ReentrantLock;
import java.util.function.Function;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -665,103 +673,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
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
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
var registry = registry().lookupOrThrow(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;
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));
}
@Override
public void removeCustomDimensions(World world) {
((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()
);
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();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), RegistrationInfo.BUILT_IN);
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.listElementIds().forEach(key -> {
var value = source.getValue(key);
var info = source.registrationInfo(key).orElse(null);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
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

@ -1,16 +1,17 @@
package com.volmit.iris.core.nms.v1_21_R4;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
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.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;
@ -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.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import lombok.SneakyThrows;
import net.minecraft.core.Registry;
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.Tag;
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.WorldLoader;
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;
@ -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.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.DimensionType;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
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;
@ -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.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;
@ -73,22 +82,23 @@ 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.List;
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.locks.ReentrantLock;
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = 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 ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
@ -663,103 +673,91 @@ public class NMSBinding implements INMSBinding {
}
@Override
@SneakyThrows
public AutoClosing injectLevelStems() {
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
var server = ((CraftServer) Bukkit.getServer());
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();
});
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
@SneakyThrows
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
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());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
return true;
} catch (Throwable e) {
Iris.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
var registry = registry().lookupOrThrow(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;
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));
}
@Override
public void removeCustomDimensions(World world) {
((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()
);
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();
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();
return new FlatLevelSource(settings);
}
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
var loc = createIrisKey(key);
target.register(key, new LevelStem(
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
source
), RegistrationInfo.BUILT_IN);
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 void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
if (source == null) return;
source.listElementIds().forEach(key -> {
var value = source.getValue(key);
var info = source.registrationInfo(key).orElse(null);
if (value != null && info != null && !target.containsKey(key))
target.register(key, value, info);
});
}
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;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
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

@ -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
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "Iris"
include(":core")
include(":core", ":core:agent")
include(
":nms:v1_21_R5",
":nms:v1_21_R4",
":nms:v1_21_R3",
":nms:v1_21_R2",