Merge pull request #1207 from VolmitSoftware/feat/byte_buddy

Feat/byte buddy
This commit is contained in:
Julian Krings 2025-07-03 23:24:30 +02:00 committed by GitHub
commit 7119fa8ef7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 1856 additions and 1342 deletions

View File

@ -90,6 +90,7 @@ nmsBindings.forEach { key, value ->
dependencies { dependencies {
compileOnly(project(":core")) compileOnly(project(":core"))
compileOnly("org.jetbrains:annotations:26.0.2") compileOnly("org.jetbrains:annotations:26.0.2")
compileOnly("net.bytebuddy:byte-buddy:1.17.5")
} }
} }
@ -105,6 +106,7 @@ nmsBindings.forEach { key, value ->
systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("net.kyori.ansi.colorLevel", color)
systemProperty("com.mojang.eula.agree", true) systemProperty("com.mojang.eula.agree", true)
systemProperty("iris.suppressReporting", !errorReporting) systemProperty("iris.suppressReporting", !errorReporting)
jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}")
} }
} }
@ -115,6 +117,7 @@ tasks {
from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) }) from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) })
} }
from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) }) from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) })
from(project(":core:agent").tasks.jar.flatMap { it.archiveFile })
archiveFileName.set("Iris-${project.version}.jar") archiveFileName.set("Iris-${project.version}.jar")
} }
@ -168,10 +171,6 @@ fun exec(vararg command: Any) {
p.waitFor() p.waitFor()
} }
dependencies {
implementation(project(":core"))
}
configurations.configureEach { configurations.configureEach {
resolutionStrategy.cacheChangingModulesFor(60, "minutes") resolutionStrategy.cacheChangingModulesFor(60, "minutes")
resolutionStrategy.cacheDynamicVersionsFor(60, "minutes") resolutionStrategy.cacheDynamicVersionsFor(60, "minutes")
@ -193,6 +192,7 @@ allprojects {
maven("https://repo.mineinabyss.com/releases") maven("https://repo.mineinabyss.com/releases")
maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/") maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/")
maven("https://repo.nexomc.com/releases/") maven("https://repo.nexomc.com/releases/")
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
} }
dependencies { dependencies {
@ -275,4 +275,4 @@ fun registerCustomOutputTaskUnix(name: String, path: String) {
into(file(path)) into(file(path))
rename { "Iris.jar" } rename { "Iris.jar" }
} }
} }

View File

@ -0,0 +1,12 @@
plugins {
java
}
tasks.jar {
manifest.attributes(
"Agent-Class" to "com.volmit.iris.util.agent.Installer",
"Premain-Class" to "com.volmit.iris.util.agent.Installer",
"Can-Redefine-Classes" to true,
"Can-Retransform-Classes" to true
)
}

View File

@ -0,0 +1,29 @@
package com.volmit.iris.util.agent;
import java.lang.instrument.Instrumentation;
public class Installer {
private static volatile Instrumentation instrumentation;
public static Instrumentation getInstrumentation() {
Instrumentation instrumentation = Installer.instrumentation;
if (instrumentation == null) {
throw new IllegalStateException("The agent is not loaded or this method is not called via the system class loader");
}
return instrumentation;
}
public static void premain(String arguments, Instrumentation instrumentation) {
doMain(instrumentation);
}
public static void agentmain(String arguments, Instrumentation instrumentation) {
doMain(instrumentation);
}
private static synchronized void doMain(Instrumentation instrumentation) {
if (Installer.instrumentation != null)
return;
Installer.instrumentation = instrumentation;
}
}

View File

@ -26,10 +26,8 @@ plugins {
val apiVersion = "1.19" val apiVersion = "1.19"
val main = "com.volmit.iris.Iris" val main = "com.volmit.iris.Iris"
repositories { val dynamic: Configuration by configurations.creating
maven("https://nexus.phoenixdevt.fr/repository/maven-public/") configurations.compileOnly { extendsFrom(dynamic) }
maven("https://repo.auxilor.io/repository/maven-public/")
}
/** /**
* Dependencies. * Dependencies.
@ -48,10 +46,6 @@ dependencies {
compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT")
compileOnly("org.apache.logging.log4j:log4j-api:2.19.0") compileOnly("org.apache.logging.log4j:log4j-api:2.19.0")
compileOnly("org.apache.logging.log4j:log4j-core:2.19.0") compileOnly("org.apache.logging.log4j:log4j-core:2.19.0")
compileOnly("commons-io:commons-io:2.13.0")
compileOnly("commons-lang:commons-lang:2.6")
compileOnly("com.github.oshi:oshi-core:5.8.5")
compileOnly("org.lz4:lz4-java:1.8.0")
// Third Party Integrations // Third Party Integrations
compileOnly("com.nexomc:nexo:1.6.0") compileOnly("com.nexomc:nexo:1.6.0")
@ -60,12 +54,13 @@ dependencies {
compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8") compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8")
compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT") compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT")
compileOnly("com.willfp:EcoItems:5.44.0") compileOnly("com.willfp:EcoItems:5.44.0")
compileOnly("io.lumine:Mythic-Dist:5.2.1")
compileOnly("io.lumine:MythicCrucible-Dist:2.0.0")
compileOnly("me.kryniowesegryderiusz:kgenerators-core:7.3") { compileOnly("me.kryniowesegryderiusz:kgenerators-core:7.3") {
isTransitive = false isTransitive = false
} }
//implementation files("libs/CustomItems.jar") //implementation files("libs/CustomItems.jar")
// Shaded // Shaded
implementation("com.dfsek:paralithic:0.8.1") implementation("com.dfsek:paralithic:0.8.1")
implementation("io.papermc:paperlib:1.0.5") implementation("io.papermc:paperlib:1.0.5")
@ -74,24 +69,22 @@ dependencies {
implementation("net.kyori:adventure-api:4.17.0") implementation("net.kyori:adventure-api:4.17.0")
implementation("org.bstats:bstats-bukkit:3.1.0") implementation("org.bstats:bstats-bukkit:3.1.0")
//implementation("org.bytedeco:javacpp:1.5.10")
//implementation("org.bytedeco:cuda-platform:12.3-8.9-1.5.10")
compileOnly("io.lumine:Mythic-Dist:5.2.1")
compileOnly("io.lumine:MythicCrucible-Dist:2.0.0")
// Dynamically Loaded // Dynamically Loaded
compileOnly("io.timeandspace:smoothie-map:2.0.2") dynamic("commons-io:commons-io:2.13.0")
compileOnly("it.unimi.dsi:fastutil:8.5.8") dynamic("commons-lang:commons-lang:2.6")
compileOnly("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") dynamic("com.github.oshi:oshi-core:6.6.5")
compileOnly("org.zeroturnaround:zt-zip:1.14") dynamic("org.lz4:lz4-java:1.8.0")
compileOnly("com.google.code.gson:gson:2.10.1") dynamic("it.unimi.dsi:fastutil:8.5.8")
compileOnly("org.ow2.asm:asm:9.2") dynamic("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2")
compileOnly("com.google.guava:guava:33.0.0-jre") dynamic("org.zeroturnaround:zt-zip:1.14")
compileOnly("bsf:bsf:2.4.0") dynamic("com.google.code.gson:gson:2.10.1")
compileOnly("rhino:js:1.7R2") dynamic("org.ow2.asm:asm:9.8")
compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.6") dynamic("bsf:bsf:2.4.0")
compileOnly("org.apache.commons:commons-lang3:3.12.0") dynamic("rhino:js:1.7R2")
compileOnly("com.github.oshi:oshi-core:6.6.5") 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")
} }
java { java {
@ -123,7 +116,8 @@ tasks {
"name" to rootProject.name, "name" to rootProject.name,
"version" to rootProject.version, "version" to rootProject.version,
"apiVersion" to apiVersion, "apiVersion" to apiVersion,
"main" to main "main" to main,
"libraries" to dynamic.allDependencies.map { "\n - $it" }.sorted().joinToString("")
) )
filesMatching("**/plugin.yml") { filesMatching("**/plugin.yml") {
expand(inputs.properties) expand(inputs.properties)

View File

@ -35,7 +35,6 @@ import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.EnginePanic;
import com.volmit.iris.engine.object.IrisCompat; import com.volmit.iris.engine.object.IrisCompat;
import com.volmit.iris.engine.object.IrisContextInjector;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.object.IrisWorld;
import com.volmit.iris.engine.platform.BukkitChunkGenerator; import com.volmit.iris.engine.platform.BukkitChunkGenerator;
@ -71,6 +70,7 @@ import com.volmit.iris.util.sentry.IrisLogger;
import com.volmit.iris.util.sentry.ServerID; import com.volmit.iris.util.sentry.ServerID;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import io.sentry.Sentry; import io.sentry.Sentry;
import lombok.NonNull;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.text.serializer.ComponentSerializer;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
@ -465,14 +465,12 @@ public class Iris extends VolmitPlugin implements Listener {
setupAudience(); setupAudience();
setupSentry(); setupSentry();
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i)); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
INMS.get();
IO.delete(new File("iris")); IO.delete(new File("iris"));
compat = IrisCompat.configured(getDataFile("compat.json")); compat = IrisCompat.configured(getDataFile("compat.json"));
ServerConfigurator.configure(); ServerConfigurator.configure();
new IrisContextInjector();
IrisSafeguard.IrisSafeguardSystem(); IrisSafeguard.IrisSafeguardSystem();
getSender().setTag(getTag()); getSender().setTag(getTag());
IrisSafeguard.earlySplash(); IrisSafeguard.splash(true);
linkMultiverseCore = new MultiverseCoreLink(); linkMultiverseCore = new MultiverseCoreLink();
linkMythicMobs = new MythicMobsLink(); linkMythicMobs = new MythicMobsLink();
configWatcher = new FileWatcher(getDataFile("settings.json")); configWatcher = new FileWatcher(getDataFile("settings.json"));
@ -487,8 +485,7 @@ public class Iris extends VolmitPlugin implements Listener {
J.sr(this::tickQueue, 0); J.sr(this::tickQueue, 0);
J.s(this::setupPapi); J.s(this::setupPapi);
J.a(ServerConfigurator::configure, 20); J.a(ServerConfigurator::configure, 20);
splash(); IrisSafeguard.splash(false);
UtilsSFG.splash();
autoStartStudio(); autoStartStudio();
checkForBukkitWorlds(); checkForBukkitWorlds();
@ -771,25 +768,11 @@ public class Iris extends VolmitPlugin implements Listener {
} }
} }
IrisDimension dim; if (id == null || id.isEmpty()) id = IrisSettings.get().getGenerator().getDefaultWorldType();
if (id == null || id.isEmpty()) {
dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType());
} else {
dim = IrisData.loadAnyDimension(id);
}
Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); Iris.debug("Generator ID: " + id + " requested by bukkit/plugin");
IrisDimension dim = loadDimension(worldName, id);
if (dim == null) { if (dim == null) {
Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); throw new RuntimeException("Can't find dimension " + id + "!");
service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false);
dim = IrisData.loadAnyDimension(id);
if (dim == null) {
throw new RuntimeException("Can't find dimension " + id + "!");
} else {
Iris.info("Resolved missing dimension, proceeding with generation.");
}
} }
Iris.debug("Assuming IrisDimension: " + dim.getName()); Iris.debug("Assuming IrisDimension: " + dim.getName());
@ -814,6 +797,24 @@ public class Iris extends VolmitPlugin implements Listener {
return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey());
} }
@Nullable
public static IrisDimension loadDimension(@NonNull String worldName, @NonNull String id) {
var data = IrisData.get(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pack")));
var dimension = data.getDimensionLoader().load(id);
if (dimension == null) dimension = IrisData.loadAnyDimension(id);
if (dimension == null) {
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
Iris.service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false);
dimension = IrisData.loadAnyDimension(id);
if (dimension != null) {
Iris.info("Resolved missing dimension, proceeding.");
}
}
return dimension;
}
public void splash() { public void splash() {
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) { if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
return; return;

View File

@ -0,0 +1,79 @@
package com.volmit.iris.core;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.io.IO;
import org.bukkit.Bukkit;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.stream.Stream;
public class IrisWorlds {
private static final AtomicCache<IrisWorlds> cache = new AtomicCache<>();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Type TYPE = TypeToken.getParameterized(KMap.class, String.class, String.class).getType();
private final KMap<String, String> worlds;
private volatile boolean dirty = false;
private IrisWorlds(KMap<String, String> worlds) {
this.worlds = worlds;
save();
}
public static IrisWorlds get() {
return cache.aquire(() -> {
File file = Iris.instance.getDataFile("worlds.json");
if (!file.exists()) {
return new IrisWorlds(new KMap<>());
}
try {
String json = IO.readAll(file);
KMap<String, String> worlds = GSON.fromJson(json, TYPE);
return new IrisWorlds(Objects.requireNonNullElseGet(worlds, KMap::new));
} catch (Throwable e) {
Iris.error("Failed to load worlds.json!");
e.printStackTrace();
Iris.reportError(e);
}
return new IrisWorlds(new KMap<>());
});
}
public void put(String name, String type) {
String old = worlds.put(name, type);
if (!type.equals(old))
dirty = true;
save();
}
public Stream<File> getFolders() {
return worlds.keySet().stream().map(k -> new File(Bukkit.getWorldContainer(), k));
}
public void clean() {
dirty = worlds.entrySet().removeIf(entry -> !new File(Bukkit.getWorldContainer(), entry.getKey() + "/iris/pack/dimensions/" + entry.getValue() + ".json").exists());
}
public synchronized void save() {
clean();
if (!dirty) return;
try {
IO.write(Iris.instance.getDataFile("worlds.json"), OutputStreamWriter::new, writer -> GSON.toJson(worlds, TYPE, writer));
dirty = false;
} catch (IOException e) {
Iris.error("Failed to save worlds.json!");
e.printStackTrace();
Iris.reportError(e);
}
}
}

View File

@ -23,10 +23,7 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.object.IrisBiome; import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.object.IrisBiomeCustom;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisRange;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.collection.KSet;
@ -34,8 +31,8 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.misc.ServerProperties;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.Data;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
@ -45,12 +42,12 @@ import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.volmit.iris.core.nms.datapack.IDataFixer.Dimension.*;
public class ServerConfigurator { public class ServerConfigurator {
public static void configure() { public static void configure() {
IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration(); IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration();
@ -115,14 +112,16 @@ public class ServerConfigurator {
KList<File> folders = getDatapacksFolder(); KList<File> folders = getDatapacksFolder();
KMap<String, KSet<String>> biomes = new KMap<>(); KMap<String, KSet<String>> biomes = new KMap<>();
allPacks().flatMap(height::merge) try (Stream<IrisData> stream = allPacks()) {
.parallel() stream.flatMap(height::merge)
.forEach(dim -> { .parallel()
Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); .forEach(dim -> {
dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>())); Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath());
}); dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>()));
dim.installDimensionType(fixer, folders);
});
}
IrisDimension.writeShared(folders, height); IrisDimension.writeShared(folders, height);
Iris.info("Data Packs Setup!"); Iris.info("Data Packs Setup!");
if (fullInstall) if (fullInstall)
@ -130,19 +129,21 @@ public class ServerConfigurator {
} }
private static void verifyDataPacksPost(boolean allowRestarting) { private static void verifyDataPacksPost(boolean allowRestarting) {
boolean bad = allPacks() try (Stream<IrisData> stream = allPacks()) {
.map(data -> { boolean bad = stream
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath()); .map(data -> {
var loader = data.getDimensionLoader(); Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
return loader.loadAll(loader.getPossibleKeys()) var loader = data.getDimensionLoader();
.stream() return loader.loadAll(loader.getPossibleKeys())
.map(ServerConfigurator::verifyDataPackInstalled) .stream()
.toList() .map(ServerConfigurator::verifyDataPackInstalled)
.contains(false); .toList()
}) .contains(false);
.toList() })
.contains(true); .toList()
if (!bad) return; .contains(true);
if (!bad) return;
}
if (allowRestarting) { if (allowRestarting) {
@ -225,9 +226,13 @@ public class ServerConfigurator {
} }
public static Stream<IrisData> allPacks() { public static Stream<IrisData> allPacks() {
return Stream.concat(listFiles(new File("plugins/Iris/packs")), return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")),
listFiles(Bukkit.getWorldContainer()).map(w -> new File(w, "iris/pack"))) IrisWorlds.get().getFolders().map(w -> new File(w, "iris/pack")))
.filter(File::isDirectory) .filter(File::isDirectory)
.filter( base -> {
var content = new File(base, "dimensions").listFiles();
return content != null && content.length > 0;
})
.map(IrisData::get); .map(IrisData::get);
} }
@ -242,20 +247,24 @@ public class ServerConfigurator {
return path.substring(worldContainer.length(), path.length() - l); return path.substring(worldContainer.length(), path.length() - l);
} }
@SneakyThrows
private static Stream<File> listFiles(File parent) { private static Stream<File> listFiles(File parent) {
var files = parent.listFiles(); if (!parent.isDirectory()) return Stream.empty();
return files == null ? Stream.empty() : Arrays.stream(files); return Files.walk(parent.toPath()).map(Path::toFile);
} }
@Data
public static class DimensionHeight { public static class DimensionHeight {
private final IDataFixer fixer; private final IDataFixer fixer;
private IrisRange overworld = new IrisRange(); private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3];
private IrisRange nether = new IrisRange();
private IrisRange end = new IrisRange(); public DimensionHeight(IDataFixer fixer) {
private int logicalOverworld = 0; this.fixer = fixer;
private int logicalNether = 0; for (int i = 0; i < 3; i++) {
private int logicalEnd = 0; dimensions[i] = new AtomicIntegerArray(new int[]{
Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE
});
}
}
public Stream<IrisDimension> merge(IrisData data) { public Stream<IrisDimension> merge(IrisData data) {
Iris.verbose("Checking Pack: " + data.getDataFolder().getPath()); Iris.verbose("Checking Pack: " + data.getDataFolder().getPath());
@ -266,25 +275,29 @@ public class ServerConfigurator {
} }
public void merge(IrisDimension dimension) { public void merge(IrisDimension dimension) {
overworld.merge(dimension.getDimensionHeight()); AtomicIntegerArray array = dimensions[dimension.getBaseDimension().ordinal()];
nether.merge(dimension.getDimensionHeight()); array.updateAndGet(0, min -> Math.min(min, dimension.getMinHeight()));
end.merge(dimension.getDimensionHeight()); array.updateAndGet(1, max -> Math.max(max, dimension.getMaxHeight()));
array.updateAndGet(2, logical -> Math.max(logical, dimension.getLogicalHeight()));
logicalOverworld = Math.max(logicalOverworld, dimension.getLogicalHeight());
logicalNether = Math.max(logicalNether, dimension.getLogicalHeightNether());
logicalEnd = Math.max(logicalEnd, dimension.getLogicalHeightEnd());
} }
public String overworldType() { public String[] jsonStrings() {
return fixer.createDimension(OVERRWORLD, overworld, logicalOverworld).toString(4); var dims = IDataFixer.Dimension.values();
var arr = new String[3];
for (int i = 0; i < 3; i++) {
arr[i] = jsonString(dims[i]);
}
return arr;
} }
public String netherType() { public String jsonString(IDataFixer.Dimension dimension) {
return fixer.createDimension(NETHER, nether, logicalNether).toString(4); var data = dimensions[dimension.ordinal()];
} int minY = data.get(0);
int maxY = data.get(1);
public String endType() { int logicalHeight = data.get(2);
return fixer.createDimension(THE_END, end, logicalEnd).toString(4); if (minY == Integer.MAX_VALUE || maxY == Integer.MIN_VALUE || Integer.MIN_VALUE == logicalHeight)
return null;
return fixer.createDimension(dimension, minY, maxY - minY, logicalHeight, null).toString(4);
} }
} }
} }

View File

@ -18,10 +18,10 @@
package com.volmit.iris.core.nms; package com.volmit.iris.core.nms;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.Mantle;
@ -36,6 +36,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color; import java.awt.Color;
@ -89,13 +90,10 @@ public interface INMSBinding {
MCABiomeContainer newBiomeContainer(int min, int max); MCABiomeContainer newBiomeContainer(int min, int max);
default World createWorld(WorldCreator c) { default World createWorld(WorldCreator c) {
if (missingDimensionTypes(true, true, true)) if (c.generator() instanceof PlatformChunkGenerator gen
throw new IllegalStateException("Missing dimenstion types to create world"); && missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey()))
throw new IllegalStateException("Missing dimension types to create world");
try (var ignored = injectLevelStems()) { return c.createWorld();
ignored.storeContext();
return c.createWorld();
}
} }
int countCustomBiomes(); int countCustomBiomes();
@ -130,13 +128,9 @@ public interface INMSBinding {
KList<String> getStructureKeys(); KList<String> getStructureKeys();
AutoClosing injectLevelStems(); boolean missingDimensionTypes(String... keys);
default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { default boolean injectBukkit() {
return null; return true;
} }
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
void removeCustomDimensions(World world);
} }

View File

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

View File

@ -1,81 +1,104 @@
package com.volmit.iris.core.nms.datapack.v1192; package com.volmit.iris.core.nms.datapack.v1192;
import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.engine.object.IrisDimensionTypeOptions;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import org.jetbrains.annotations.Nullable;
import java.util.Map; import java.util.Map;
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
public class DataFixerV1192 implements IDataFixer { public class DataFixerV1192 implements IDataFixer {
private static final Map<Dimension, IrisDimensionTypeOptions> OPTIONS = Map.of(
Dimension.OVERWORLD, new IrisDimensionTypeOptions(
FALSE,
TRUE,
FALSE,
FALSE,
TRUE,
TRUE,
TRUE,
FALSE,
1d,
0f,
null,
192,
0),
Dimension.NETHER, new IrisDimensionTypeOptions(
TRUE,
FALSE,
TRUE,
TRUE,
FALSE,
FALSE,
FALSE,
TRUE,
8d,
0.1f,
18000L,
null,
15),
Dimension.END, new IrisDimensionTypeOptions(
FALSE,
FALSE,
FALSE,
FALSE,
FALSE,
TRUE,
FALSE,
FALSE,
1d,
0f,
6000L,
null,
0)
);
private static final Map<Dimension, String> DIMENSIONS = Map.of( private static final Map<Dimension, String> DIMENSIONS = Map.of(
Dimension.OVERRWORLD, """ Dimension.OVERWORLD, """
{ {
"ambient_light": 0.0,
"bed_works": true,
"coordinate_scale": 1.0,
"effects": "minecraft:overworld", "effects": "minecraft:overworld",
"has_ceiling": false,
"has_raids": true,
"has_skylight": true,
"infiniburn": "#minecraft:infiniburn_overworld", "infiniburn": "#minecraft:infiniburn_overworld",
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": { "monster_spawn_light_level": {
"type": "minecraft:uniform", "type": "minecraft:uniform",
"value": { "value": {
"max_inclusive": 7, "max_inclusive": 7,
"min_inclusive": 0 "min_inclusive": 0
} }
}, }
"natural": true,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}""", }""",
Dimension.NETHER, """ Dimension.NETHER, """
{ {
"ambient_light": 0.1,
"bed_works": false,
"coordinate_scale": 8.0,
"effects": "minecraft:the_nether", "effects": "minecraft:the_nether",
"fixed_time": 18000,
"has_ceiling": true,
"has_raids": false,
"has_skylight": false,
"infiniburn": "#minecraft:infiniburn_nether", "infiniburn": "#minecraft:infiniburn_nether",
"monster_spawn_block_light_limit": 15,
"monster_spawn_light_level": 7, "monster_spawn_light_level": 7,
"natural": false,
"piglin_safe": true,
"respawn_anchor_works": true,
"ultrawarm": true
}""", }""",
Dimension.THE_END, """ Dimension.END, """
{ {
"ambient_light": 0.0,
"bed_works": false,
"coordinate_scale": 1.0,
"effects": "minecraft:the_end", "effects": "minecraft:the_end",
"fixed_time": 6000,
"has_ceiling": false,
"has_raids": true,
"has_skylight": false,
"infiniburn": "#minecraft:infiniburn_end", "infiniburn": "#minecraft:infiniburn_end",
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": { "monster_spawn_light_level": {
"type": "minecraft:uniform", "type": "minecraft:uniform",
"value": { "value": {
"max_inclusive": 7, "max_inclusive": 7,
"min_inclusive": 0 "min_inclusive": 0
} }
}, }
"natural": false,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}""" }"""
); );
@Override @Override
public JSONObject rawDimension(Dimension dimension) { public JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options) {
return new JSONObject(DIMENSIONS.get(dimension)); return options == null ? OPTIONS.get(dimension).toJson() : options.resolve(OPTIONS.get(dimension)).toJson();
}
@Override
public void fixDimension(Dimension dimension, JSONObject json) {
var missing = new JSONObject(DIMENSIONS.get(dimension));
for (String key : missing.keySet()) {
if (json.has(key)) continue;
json.put(key, missing.get(key));
}
} }
} }

View File

@ -45,13 +45,12 @@ public class DataFixerV1206 extends DataFixerV1192 {
} }
@Override @Override
public JSONObject rawDimension(Dimension dimension) { public void fixDimension(Dimension dimension, JSONObject json) {
JSONObject json = super.rawDimension(dimension); super.fixDimension(dimension, json);
if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel)) if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel))
return json; return;
var value = (JSONObject) lightLevel.remove("value"); var value = (JSONObject) lightLevel.remove("value");
lightLevel.put("max_inclusive", value.get("max_inclusive")); lightLevel.put("max_inclusive", value.get("max_inclusive"));
lightLevel.put("min_inclusive", value.get("min_inclusive")); lightLevel.put("min_inclusive", value.get("min_inclusive"));
return json;
} }
} }

View File

@ -20,9 +20,7 @@ package com.volmit.iris.core.nms.v1X;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
@ -121,25 +119,10 @@ public class NMSBinding1X implements INMSBinding {
} }
@Override @Override
public AutoClosing injectLevelStems() { public boolean missingDimensionTypes(String... keys) {
return new AutoClosing(() -> {});
}
@Override
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
return injectLevelStems();
}
@Override
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
return false; return false;
} }
@Override
public void removeCustomDimensions(World world) {
}
@Override @Override
public CompoundTag serializeEntity(Entity location) { public CompoundTag serializeEntity(Entity location) {
return null; return null;

View File

@ -3,7 +3,10 @@ package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import java.util.concurrent.atomic.AtomicBoolean;
public class IrisSafeguard { public class IrisSafeguard {
private static final AtomicBoolean sfg = new AtomicBoolean(false);
public static boolean unstablemode = false; public static boolean unstablemode = false;
public static boolean warningmode = false; public static boolean warningmode = false;
public static boolean stablemode = false; public static boolean stablemode = false;
@ -13,12 +16,14 @@ public class IrisSafeguard {
ServerBootSFG.BootCheck(); ServerBootSFG.BootCheck();
} }
public static void earlySplash() { public static void splash(boolean early) {
if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode) if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode))
return; return;
Iris.instance.splash(); if (!sfg.getAndSet(true)) {
UtilsSFG.splash(); Iris.instance.splash();
UtilsSFG.splash();
}
} }
public static String mode() { public static String mode() {

View File

@ -1,10 +1,15 @@
package com.volmit.iris.core.safeguard; package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.engine.object.IrisContextInjector; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.misc.ServerProperties;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
@ -15,10 +20,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import static com.volmit.iris.Iris.getJavaVersion; import static com.volmit.iris.Iris.getJavaVersion;
import static com.volmit.iris.core.safeguard.IrisSafeguard.*; import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
@ -31,6 +33,8 @@ public class ServerBootSFG {
public static boolean hasPrivileges = true; public static boolean hasPrivileges = true;
public static boolean unsuportedversion = false; public static boolean unsuportedversion = false;
public static boolean missingDimensionTypes = false; public static boolean missingDimensionTypes = false;
public static boolean missingAgent = false;
public static boolean failedInjection = false;
protected static boolean safeguardPassed; protected static boolean safeguardPassed;
public static boolean passedserversoftware = true; public static boolean passedserversoftware = true;
protected static int count; protected static int count;
@ -112,10 +116,21 @@ public class ServerBootSFG {
severityMedium++; severityMedium++;
} }
if (IrisContextInjector.isMissingDimensionTypes()) { if (!Agent.install()) {
missingDimensionTypes = true; missingAgent = true;
joiner.add("Missing Dimension Types"); joiner.add("Missing Java Agent");
severityHigh++; severityHigh++;
} else {
if (missingDimensionTypes()) {
missingDimensionTypes = true;
joiner.add("Missing Dimension Types");
severityHigh++;
}
if (!INMS.get().injectBukkit()) {
failedInjection = true;
joiner.add("Failed Bukkit Injection");
severityHigh++;
}
} }
allIncompatibilities = joiner.toString(); allIncompatibilities = joiner.toString();
@ -169,4 +184,32 @@ public class ServerBootSFG {
return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists()); return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists());
} }
private static boolean missingDimensionTypes() {
return INMS.get().missingDimensionTypes(getDimensionTypes().toArray(String[]::new));
}
private static KSet<String> getDimensionTypes() {
var bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
var worlds = bukkit.getConfigurationSection("worlds");
if (worlds == null) return new KSet<>();
var types = new KSet<String>();
for (String world : worlds.getKeys(false)) {
var gen = worlds.getString(world + ".generator");
if (gen == null) continue;
String loadKey;
if (gen.equalsIgnoreCase("iris")) {
loadKey = IrisSettings.get().getGenerator().getDefaultWorldType();
} else if (gen.startsWith("Iris:")) {
loadKey = gen.substring(5);
} else continue;
IrisDimension dimension = Iris.loadDimension(world, loadKey);
if (dimension == null) continue;
types.add(dimension.getDimensionTypeKey());
}
return types;
}
} }

View File

@ -1,6 +1,7 @@
package com.volmit.iris.core.safeguard; package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
public class UtilsSFG { public class UtilsSFG {
@ -44,6 +45,11 @@ public class UtilsSFG {
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded."); Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support."); Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
} }
if (ServerBootSFG.missingAgent) {
Iris.safeguard(C.RED + "Java Agent");
Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments.");
Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath());
}
if (!ServerBootSFG.passedserversoftware) { if (!ServerBootSFG.passedserversoftware) {
Iris.safeguard(C.YELLOW + "Unsupported Server Software"); Iris.safeguard(C.YELLOW + "Unsupported Server Software");
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead."); Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");

View File

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

@ -25,6 +25,7 @@ import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.core.nms.datapack.IDataFixer.Dimension;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
@ -45,8 +46,7 @@ import org.bukkit.Material;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import java.io.File; import java.io.*;
import java.io.IOException;
@Accessors(chain = true) @Accessors(chain = true)
@AllArgsConstructor @AllArgsConstructor
@ -74,10 +74,6 @@ public class IrisDimension extends IrisRegistrant {
@MaxNumber(2032) @MaxNumber(2032)
@Desc("Maximum height at which players can be teleported to through gameplay.") @Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeight = 256; private int logicalHeight = 256;
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeightEnd = 256;
@Desc("Maximum height at which players can be teleported to through gameplay.")
private int logicalHeightNether = 256;
@RegistryListResource(IrisJigsawStructure.class) @RegistryListResource(IrisJigsawStructure.class)
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.") @Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
private String stronghold; private String stronghold;
@ -166,10 +162,8 @@ public class IrisDimension extends IrisRegistrant {
private int fluidHeight = 63; private int fluidHeight = 63;
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
private IrisRange dimensionHeight = new IrisRange(-64, 320); private IrisRange dimensionHeight = new IrisRange(-64, 320);
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") @Desc("Define options for this dimension")
private IrisRange dimensionHeightEnd = new IrisRange(-64, 320); private IrisDimensionTypeOptions dimensionOptions = new IrisDimensionTypeOptions();
@Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.")
private IrisRange dimensionHeightNether = new IrisRange(-64, 320);
@RegistryListResource(IrisBiome.class) @RegistryListResource(IrisBiome.class)
@Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.") @Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.")
private String focus = ""; private String focus = "";
@ -412,6 +406,39 @@ public class IrisDimension extends IrisRegistrant {
}); });
} }
public Dimension getBaseDimension() {
return switch (getEnvironment()) {
case NETHER -> Dimension.NETHER;
case THE_END -> Dimension.END;
default -> Dimension.OVERWORLD;
};
}
public String getDimensionTypeKey() {
return getDimensionType().key();
}
public IrisDimensionType getDimensionType() {
return new IrisDimensionType(getBaseDimension(), getDimensionOptions(), getLogicalHeight(), getMaxHeight() - getMinHeight(), getMinHeight());
}
public void installDimensionType(IDataFixer fixer, KList<File> folders) {
IrisDimensionType type = getDimensionType();
String json = type.toJson(fixer);
Iris.verbose(" Installing Data Pack Dimension Type: \"iris:" + type.key() + '"');
for (File datapacks : folders) {
File output = new File(datapacks, "iris/data/iris/dimension_type/" + type.key() + ".json");
output.getParentFile().mkdirs();
try {
IO.writeAll(output, json);
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
}
}
@Override @Override
public String getFolderName() { public String getFolderName() {
return "dimensions"; return "dimensions";
@ -428,11 +455,12 @@ public class IrisDimension extends IrisRegistrant {
} }
public static void writeShared(KList<File> folders, DimensionHeight height) { public static void writeShared(KList<File> folders, DimensionHeight height) {
Iris.verbose(" Installing Data Pack Dimension Types: \"iris:overworld\", \"iris:the_nether\", \"iris:the_end\""); Iris.verbose(" Installing Data Pack Vanilla Dimension Types");
String[] jsonStrings = height.jsonStrings();
for (File datapacks : folders) { for (File datapacks : folders) {
write(datapacks, "overworld", height.overworldType()); write(datapacks, "overworld", jsonStrings[0]);
write(datapacks, "the_nether", height.netherType()); write(datapacks, "the_nether", jsonStrings[1]);
write(datapacks, "the_end", height.endType()); write(datapacks, "the_end", jsonStrings[2]);
} }
String raw = """ String raw = """
@ -457,17 +485,9 @@ public class IrisDimension extends IrisRegistrant {
} }
private static void write(File datapacks, String type, String json) { private static void write(File datapacks, String type, String json) {
File dimType = new File(datapacks, "iris/data/iris/dimension_type/" + type + ".json"); if (json == null) return;
File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json"); File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json");
dimType.getParentFile().mkdirs();
try {
IO.writeAll(dimType, json);
} catch (IOException e) {
Iris.reportError(e);
e.printStackTrace();
}
if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) { if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) {
dimTypeVanilla.getParentFile().mkdirs(); dimTypeVanilla.getParentFile().mkdirs();
try { try {

View File

@ -0,0 +1,91 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.nms.datapack.IDataFixer;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.io.IO;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.*;
@Getter
@ToString
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode
public final class IrisDimensionType {
@NonNull
private final String key;
@NonNull
private final IDataFixer.Dimension base;
@NonNull
private final IrisDimensionTypeOptions options;
private final int logicalHeight;
private final int height;
private final int minY;
public IrisDimensionType(
@NonNull IDataFixer.Dimension base,
@NonNull IrisDimensionTypeOptions options,
int logicalHeight,
int height,
int minY
) {
if (logicalHeight > height) throw new IllegalArgumentException("Logical height cannot be greater than height");
if (logicalHeight < 0) throw new IllegalArgumentException("Logical height cannot be less than zero");
if (height < 16 || height > 4064 ) throw new IllegalArgumentException("Height must be between 16 and 4064");
if ((height & 15) != 0) throw new IllegalArgumentException("Height must be a multiple of 16");
if (minY < -2032 || minY > 2031) throw new IllegalArgumentException("Min Y must be between -2032 and 2031");
if ((minY & 15) != 0) throw new IllegalArgumentException("Min Y must be a multiple of 16");
this.base = base;
this.options = options;
this.logicalHeight = logicalHeight;
this.height = height;
this.minY = minY;
this.key = createKey();
}
public static IrisDimensionType fromKey(String key) {
var stream = new ByteArrayInputStream(IO.decode(key.replace(".", "=").toUpperCase()));
try (var din = new DataInputStream(stream)) {
return new IrisDimensionType(
IDataFixer.Dimension.values()[din.readUnsignedByte()],
new IrisDimensionTypeOptions().read(din),
Varint.readUnsignedVarInt(din),
Varint.readUnsignedVarInt(din),
Varint.readSignedVarInt(din)
);
} catch (IOException e) {
throw new RuntimeException("This is impossible", e);
}
}
public String toJson(IDataFixer fixer) {
return fixer.createDimension(
base,
minY,
height,
logicalHeight,
options.copy()
).toString(4);
}
private String createKey() {
var stream = new ByteArrayOutputStream(41);
try (var dos = new DataOutputStream(stream)) {
dos.writeByte(base.ordinal());
options.write(dos);
Varint.writeUnsignedVarInt(logicalHeight, dos);
Varint.writeUnsignedVarInt(height, dos);
Varint.writeSignedVarInt(minY, dos);
} catch (IOException e) {
throw new RuntimeException("This is impossible", e);
}
return IO.encode(stream.toByteArray()).replace("=", ".").toLowerCase();
}
public IrisDimensionTypeOptions options() {
return options.copy();
}
}

View File

@ -0,0 +1,320 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.MaxNumber;
import com.volmit.iris.engine.object.annotations.MinNumber;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.json.JSONObject;
import lombok.*;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*;
@Data
@NoArgsConstructor
@Accessors(fluent = true, chain = true)
@Desc("Optional options for the dimension type")
public class IrisDimensionTypeOptions {
@MinNumber(0.00001)
@MaxNumber(30000000)
@Desc("The multiplier applied to coordinates when leaving the dimension. Value between 0.00001 and 30000000.0 (both inclusive).")
private double coordinateScale = -1;
@MinNumber(0)
@MaxNumber(1)
@Desc("How much light the dimension has. When set to 0, it completely follows the light level; when set to 1, there is no ambient lighting.")
private float ambientLight = -1;
@Nullable
@MinNumber(0)
@MaxNumber(Long.MAX_VALUE)
@Desc("If this is set to an int, the time of the day is the specified value. To ensure a normal time cycle, set it to null.")
private Long fixedTime = -1L;
@Nullable
@MinNumber(-2032)
@MaxNumber(2031)
@Desc("Optional value between -2032 and 2031. If set, determines the lower edge of the clouds. If set to null, clouds are disabled in the dimension.")
private Integer cloudHeight = -1;
@MinNumber(0)
@MaxNumber(15)
@Desc("Value between 0 and 15 (both inclusive). Maximum block light required when the monster spawns.")
private int monsterSpawnBlockLightLimit = -1;
@NonNull
@Desc("Whether the dimensions behaves like the nether (water evaporates and sponges dry) or not. Also lets stalactites drip lava and causes lava to spread faster and thinner.")
private TriState ultrawarm = DEFAULT;
@NonNull
@Desc("When false, compasses spin randomly, and using a bed to set the respawn point or sleep, is disabled. When true, nether portals can spawn zombified piglins, and creaking hearts can spawn creakings.")
private TriState natural = DEFAULT;
@NonNull
@Desc("When false, Piglins and hoglins shake and transform to zombified entities.")
private TriState piglinSafe = DEFAULT;
@NonNull
@Desc("When false, the respawn anchor blows up when trying to set spawn point.")
private TriState respawnAnchorWorks = DEFAULT;
@NonNull
@Desc("When false, the bed blows up when trying to sleep.")
private TriState bedWorks = DEFAULT;
@NonNull
@Desc("Whether players with the Bad Omen effect can cause a raid.")
private TriState raids = DEFAULT;
@NonNull
@Desc("Whether the dimension has skylight or not.")
private TriState skylight = DEFAULT;
@NonNull
@Desc("Whether the dimension has a bedrock ceiling. Note that this is only a logical ceiling. It is unrelated with whether the dimension really has a block ceiling.")
private TriState ceiling = DEFAULT;
public IrisDimensionTypeOptions(
@NonNull TriState ultrawarm,
@NonNull TriState natural,
@NonNull TriState piglinSafe,
@NonNull TriState respawnAnchorWorks,
@NonNull TriState bedWorks,
@NonNull TriState raids,
@NonNull TriState skylight,
@NonNull TriState ceiling,
double coordinateScale,
float ambientLight,
@Nullable Long fixedTime,
@Nullable Integer cloudHeight,
int monsterSpawnBlockLightLimit
) {
if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000))
throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000");
if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1))
throw new IllegalArgumentException("Ambient light must be between 0 and 1");
if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031))
throw new IllegalArgumentException("Cloud height must be between -2032 and 2031");
if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15))
throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15");
this.ultrawarm = ultrawarm;
this.natural = natural;
this.piglinSafe = piglinSafe;
this.respawnAnchorWorks = respawnAnchorWorks;
this.bedWorks = bedWorks;
this.raids = raids;
this.skylight = skylight;
this.ceiling = ceiling;
this.coordinateScale = coordinateScale;
this.ambientLight = ambientLight;
this.fixedTime = fixedTime;
this.cloudHeight = cloudHeight;
this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit;
}
public IrisDimensionTypeOptions coordinateScale(double coordinateScale) {
if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000))
throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000");
this.coordinateScale = coordinateScale;
return this;
}
public IrisDimensionTypeOptions ambientLight(float ambientLight) {
if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1))
throw new IllegalArgumentException("Ambient light must be between 0 and 1");
this.ambientLight = ambientLight;
return this;
}
public IrisDimensionTypeOptions cloudHeight(@Nullable Integer cloudHeight) {
if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031))
throw new IllegalArgumentException("Cloud height must be between -2032 and 2031");
this.cloudHeight = cloudHeight;
return this;
}
public IrisDimensionTypeOptions monsterSpawnBlockLightLimit(int monsterSpawnBlockLightLimit) {
if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15))
throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15");
this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit;
return this;
}
public void write(DataOutput dos) throws IOException {
int bits = 0;
int index = 0;
for (TriState state : new TriState[]{
ultrawarm,
natural,
skylight,
ceiling,
piglinSafe,
bedWorks,
respawnAnchorWorks,
raids
}) {
if (state == DEFAULT) {
index++;
continue;
}
bits |= (short) (1 << index++);
if (state == TRUE)
bits |= (short) (1 << index++);
}
if (coordinateScale != -1)
bits |= (1 << index++);
if (ambientLight != -1)
bits |= (1 << index++);
if (monsterSpawnBlockLightLimit != -1)
bits |= (1 << index++);
if (fixedTime != null) {
bits |= (1 << index++);
if (fixedTime != -1L)
bits |= (1 << index++);
}
if (cloudHeight != null) {
bits |= (1 << index++);
if (cloudHeight != -1)
bits |= (1 << index);
}
Varint.writeSignedVarInt(bits, dos);
if (coordinateScale != -1)
Varint.writeUnsignedVarLong(Double.doubleToLongBits(coordinateScale), dos);
if (ambientLight != -1)
Varint.writeUnsignedVarInt(Float.floatToIntBits(ambientLight), dos);
if (monsterSpawnBlockLightLimit != -1)
Varint.writeSignedVarInt(monsterSpawnBlockLightLimit, dos);
if (fixedTime != null && fixedTime != -1L)
Varint.writeSignedVarLong(fixedTime, dos);
if (cloudHeight != null && cloudHeight != -1)
Varint.writeSignedVarInt(cloudHeight, dos);
}
public IrisDimensionTypeOptions read(DataInput dis) throws IOException {
TriState[] states = new TriState[8];
Arrays.fill(states, DEFAULT);
int bits = Varint.readSignedVarInt(dis);
int index = 0;
for (int i = 0; i < 8; i++) {
if ((bits & (1 << index++)) == 0)
continue;
states[i] = (bits & (1 << index++)) == 0 ? FALSE : TRUE;
}
ultrawarm = states[0];
natural = states[1];
skylight = states[2];
ceiling = states[3];
piglinSafe = states[4];
bedWorks = states[5];
respawnAnchorWorks = states[6];
raids = states[7];
coordinateScale = (bits & (1 << index++)) != 0 ? Double.longBitsToDouble(Varint.readUnsignedVarLong(dis)) : -1;
ambientLight = (bits & (1 << index++)) != 0 ? Float.intBitsToFloat(Varint.readUnsignedVarInt(dis)) : -1;
monsterSpawnBlockLightLimit = (bits & (1 << index++)) != 0 ? Varint.readSignedVarInt(dis) : -1;
fixedTime = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarLong(dis) : -1L : null;
cloudHeight = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarInt(dis) : -1 : null;
return this;
}
public IrisDimensionTypeOptions resolve(IrisDimensionTypeOptions other) {
if (ultrawarm == DEFAULT)
ultrawarm = other.ultrawarm;
if (natural == DEFAULT)
natural = other.natural;
if (piglinSafe == DEFAULT)
piglinSafe = other.piglinSafe;
if (respawnAnchorWorks == DEFAULT)
respawnAnchorWorks = other.respawnAnchorWorks;
if (bedWorks == DEFAULT)
bedWorks = other.bedWorks;
if (raids == DEFAULT)
raids = other.raids;
if (skylight == DEFAULT)
skylight = other.skylight;
if (ceiling == DEFAULT)
ceiling = other.ceiling;
if (coordinateScale == -1)
coordinateScale = other.coordinateScale;
if (ambientLight == -1)
ambientLight = other.ambientLight;
if (fixedTime != null && fixedTime == -1L)
fixedTime = other.fixedTime;
if (cloudHeight != null && cloudHeight == -1)
cloudHeight = other.cloudHeight;
if (monsterSpawnBlockLightLimit == -1)
monsterSpawnBlockLightLimit = other.monsterSpawnBlockLightLimit;
return this;
}
public JSONObject toJson() {
if (!isComplete()) throw new IllegalStateException("Cannot serialize incomplete options");
JSONObject json = new JSONObject();
json.put("ultrawarm", ultrawarm.bool());
json.put("natural", natural.bool());
json.put("piglin_safe", piglinSafe.bool());
json.put("respawn_anchor_works", respawnAnchorWorks.bool());
json.put("bed_works", bedWorks.bool());
json.put("has_raids", raids.bool());
json.put("has_skylight", skylight.bool());
json.put("has_ceiling", ceiling.bool());
json.put("coordinate_scale", coordinateScale);
json.put("ambient_light", ambientLight);
json.put("monster_spawn_block_light_limit", monsterSpawnBlockLightLimit);
if (fixedTime != null) json.put("fixed_time", fixedTime);
if (cloudHeight != null) json.put("cloud_height", cloudHeight);
return json;
}
public IrisDimensionTypeOptions copy() {
return new IrisDimensionTypeOptions(
ultrawarm,
natural,
piglinSafe,
respawnAnchorWorks,
bedWorks,
raids,
skylight,
ceiling,
coordinateScale,
ambientLight,
fixedTime,
cloudHeight,
monsterSpawnBlockLightLimit
);
}
public boolean isComplete() {
return ultrawarm != DEFAULT
&& natural != DEFAULT
&& piglinSafe != DEFAULT
&& respawnAnchorWorks != DEFAULT
&& bedWorks != DEFAULT
&& raids != DEFAULT
&& skylight != DEFAULT
&& ceiling != DEFAULT
&& coordinateScale != -1
&& ambientLight != -1
&& monsterSpawnBlockLightLimit != -1
&& (fixedTime == null || fixedTime != -1L)
&& (cloudHeight == null || cloudHeight != -1);
}
@Desc("Allows reusing the behavior of the base dimension")
public enum TriState {
@Desc("Follow the behavior of the base dimension")
DEFAULT,
@Desc("True")
TRUE,
@Desc("False")
FALSE;
public boolean bool() {
return this == TRUE;
}
}
}

View File

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

View File

@ -24,21 +24,23 @@ import com.volmit.iris.engine.framework.EngineTarget;
import com.volmit.iris.engine.framework.Hotloadable; import com.volmit.iris.engine.framework.Hotloadable;
import com.volmit.iris.util.data.DataProvider; import com.volmit.iris.util.data.DataProvider;
import org.bukkit.World; import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
public interface PlatformChunkGenerator extends Hotloadable, DataProvider { public interface PlatformChunkGenerator extends Hotloadable, DataProvider {
@Nullable
Engine getEngine(); Engine getEngine();
@Override @Override
default IrisData getData() { default IrisData getData() {
return getEngine().getData(); return getTarget().getData();
} }
default EngineTarget getTarget() { @NotNull
return getEngine().getTarget(); EngineTarget getTarget();
}
void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs); void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs);

View File

@ -0,0 +1,46 @@
package com.volmit.iris.util.agent;
import com.volmit.iris.Iris;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class Agent {
private static final String NAME = "com.volmit.iris.util.agent.Installer";
public static final File AGENT_JAR = new File(Iris.instance.getDataFolder(), "agent.jar");
public static ClassReloadingStrategy installed() {
return ClassReloadingStrategy.of(getInstrumentation());
}
public static Instrumentation getInstrumentation() {
Instrumentation instrumentation = doGetInstrumentation();
if (instrumentation == null) throw new IllegalStateException("The agent is not initialized or unavailable");
return instrumentation;
}
public static boolean install() {
if (doGetInstrumentation() != null)
return true;
try {
Files.copy(Iris.instance.getResource("agent.jar"), AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING);
Iris.info("Installing Java Agent...");
ByteBuddyAgent.attach(AGENT_JAR, ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE);
} catch (Throwable e) {
e.printStackTrace();
}
return doGetInstrumentation() != null;
}
private static Instrumentation doGetInstrumentation() {
try {
return (Instrumentation) Class.forName(NAME, true, ClassLoader.getSystemClassLoader()).getMethod("getInstrumentation").invoke(null);
} catch (Exception ex) {
return null;
}
}
}

View File

@ -1643,7 +1643,7 @@ public class IO {
return (ch2 == -1); return (ch2 == -1);
} }
public static <T extends OutputStream> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException { public static <T extends Closeable> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException {
File dir = new File(file.getParentFile(), ".tmp"); File dir = new File(file.getParentFile(), ".tmp");
dir.mkdirs(); dir.mkdirs();
dir.deleteOnExit(); dir.deleteOnExit();

View File

@ -5,21 +5,7 @@ load: STARTUP
authors: [ cyberpwn, NextdoorPsycho, Vatuu ] authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
website: volmit.com website: volmit.com
description: More than a Dimension! description: More than a Dimension!
libraries: libraries: ${libraries}
- com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2
- com.github.ben-manes.caffeine:caffeine:3.0.5
- org.apache.commons:commons-lang3:3.12.0
- commons-io:commons-io:2.13.0
- io.timeandspace:smoothie-map:2.0.2
- com.google.guava:guava:31.0.1-jre
- com.google.code.gson:gson:2.10.1
- org.zeroturnaround:zt-zip:1.14
- it.unimi.dsi:fastutil:8.5.6
- org.ow2.asm:asm:9.2
- rhino:js:1.7R2
- bsf:bsf:2.4.0
- org.lz4:lz4-java:1.8.0
- com.github.oshi:oshi-core:6.6.5
commands: commands:
iris: iris:
aliases: [ ir, irs ] aliases: [ ir, irs ]

View File

@ -2,42 +2,45 @@ package com.volmit.iris.core.nms.v1_20_R1;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.io.NBTUtil;
import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy;
import net.minecraft.core.*; import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.RandomSequences;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
@ -47,15 +50,16 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
@ -63,31 +67,26 @@ import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlock;
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftDolphin;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.entity.EntityType; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import sun.misc.Unsafe;
import java.awt.*;
import java.awt.Color; import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
@ -95,11 +94,10 @@ public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -645,103 +643,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> new ResourceLocation("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().K.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), Lifecycle.stable());
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.registryKeySet().forEach(key -> { static void enter(
var value = source.get(key); @Advice.Argument(0) MinecraftServer server,
var info = source.lifecycle(value); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return new ResourceLocation("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,41 +1,63 @@
package com.volmit.iris.core.nms.v1_20_R2; package com.volmit.iris.core.nms.v1_20_R2;
import java.awt.Color; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.*; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -45,58 +67,36 @@ import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftDolphin;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.awt.*;
import com.volmit.iris.Iris; import java.awt.Color;
import com.volmit.iris.core.nms.INMSBinding; import java.lang.reflect.Field;
import com.volmit.iris.engine.data.cache.AtomicCache; import java.lang.reflect.InvocationTargetException;
import com.volmit.iris.engine.framework.Engine; import java.lang.reflect.Method;
import com.volmit.iris.util.collection.KList; import java.lang.reflect.Modifier;
import com.volmit.iris.util.collection.KMap; import java.util.*;
import com.volmit.iris.util.hunk.Hunk; import java.util.List;
import com.volmit.iris.util.json.JSONObject; import java.util.concurrent.Executor;
import com.volmit.iris.util.mantle.Mantle; import java.util.concurrent.atomic.AtomicBoolean;
import com.volmit.iris.util.math.Vector3d; import java.util.concurrent.atomic.AtomicInteger;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.io.NBTUtil;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import sun.misc.Unsafe;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -624,6 +624,14 @@ public class NMSBinding implements INMSBinding {
return keys; return keys;
} }
@Override
public boolean missingDimensionTypes(String... keys) {
var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
return !Arrays.stream(keys)
.map(key -> new ResourceLocation("iris", key))
.allMatch(type::containsKey);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@ -646,103 +654,83 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectLevelStems() { if (injected.getAndSet(true))
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return true;
try {
Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var server = ((CraftServer) Bukkit.getServer()); return true;
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); } catch (Throwable e) {
var nmsServer = server.getServer(); Iris.error(C.RED + "Failed to inject Bukkit");
var old = nmsServer.worldLoader; e.printStackTrace();
}
field.setAccessible(true); return false;
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
@SneakyThrows if (!(raw instanceof PlatformChunkGenerator gen))
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { throw new IllegalStateException("Generator is not platform chunk generator!");
var reg = registry();
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
field.setAccessible(true);
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
var injected = access.registryOrThrow(Registries.LEVEL_STEM); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); return new LevelStem(dimensionType, chunkGenerator(access));
var fake = new HashMap<>(old);
fake.put(Registries.LEVEL_STEM, injected);
field.set(reg, fake);
return new AutoClosing(() -> field.set(reg, old));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
return overworld || nether || end;
}
@Override
public void removeCustomDimensions(World world) {
((CraftWorld) world).getHandle().K.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), Lifecycle.stable());
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.registryKeySet().forEach(key -> { static void enter(
var value = source.get(key); @Advice.Argument(0) MinecraftServer server,
var info = source.lifecycle(value); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return new ResourceLocation("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,41 +1,63 @@
package com.volmit.iris.core.nms.v1_20_R3; package com.volmit.iris.core.nms.v1_20_R3;
import java.awt.Color; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.*; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -45,58 +67,36 @@ import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftDolphin;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.awt.*;
import com.volmit.iris.Iris; import java.awt.Color;
import com.volmit.iris.core.nms.INMSBinding; import java.lang.reflect.Field;
import com.volmit.iris.engine.data.cache.AtomicCache; import java.lang.reflect.InvocationTargetException;
import com.volmit.iris.engine.framework.Engine; import java.lang.reflect.Method;
import com.volmit.iris.util.collection.KList; import java.lang.reflect.Modifier;
import com.volmit.iris.util.collection.KMap; import java.util.*;
import com.volmit.iris.util.hunk.Hunk; import java.util.List;
import com.volmit.iris.util.json.JSONObject; import java.util.concurrent.Executor;
import com.volmit.iris.util.mantle.Mantle; import java.util.concurrent.atomic.AtomicBoolean;
import com.volmit.iris.util.math.Vector3d; import java.util.concurrent.atomic.AtomicInteger;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.io.NBTUtil;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import sun.misc.Unsafe;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -647,103 +647,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> new ResourceLocation("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().K.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), Lifecycle.stable());
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.registryKeySet().forEach(key -> { static void enter(
var value = source.get(key); @Advice.Argument(0) MinecraftServer server,
var info = source.lifecycle(value); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return new ResourceLocation("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,53 +1,64 @@
package com.volmit.iris.core.nms.v1_20_R4; package com.volmit.iris.core.nms.v1_20_R4;
import java.awt.Color; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.*; import net.minecraft.core.*;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.EndTag;
import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.ShortTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.commands.data.DataCommands;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -56,55 +67,37 @@ import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockType;
import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin;
import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.awt.*;
import com.volmit.iris.Iris; import java.awt.Color;
import com.volmit.iris.core.nms.INMSBinding; import java.lang.reflect.Field;
import com.volmit.iris.engine.data.cache.AtomicCache; import java.lang.reflect.InvocationTargetException;
import com.volmit.iris.engine.framework.Engine; import java.lang.reflect.Method;
import com.volmit.iris.util.collection.KList; import java.lang.reflect.Modifier;
import com.volmit.iris.util.collection.KMap; import java.util.*;
import com.volmit.iris.util.hunk.Hunk; import java.util.List;
import com.volmit.iris.util.json.JSONObject; import java.util.concurrent.Executor;
import com.volmit.iris.util.mantle.Mantle; import java.util.concurrent.atomic.AtomicBoolean;
import com.volmit.iris.util.math.Vector3d; import java.util.concurrent.atomic.AtomicInteger;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -672,103 +665,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> new ResourceLocation("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().K.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), RegistrationInfo.BUILT_IN);
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.registryKeySet().forEach(key -> { static void enter(
var value = source.get(key); @Advice.Argument(0) MinecraftServer server,
var info = source.registrationInfo(key).orElse(null); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return new ResourceLocation("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,47 +1,68 @@
package com.volmit.iris.core.nms.v1_21_R1; package com.volmit.iris.core.nms.v1_21_R1;
import java.awt.Color; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.*; import it.unimi.dsi.fastutil.shorts.ShortList;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -51,57 +72,36 @@ import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockState; import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftDolphin;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R1.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_21_R1.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.awt.*;
import com.volmit.iris.Iris; import java.awt.Color;
import com.volmit.iris.core.nms.INMSBinding; import java.lang.reflect.Field;
import com.volmit.iris.engine.data.cache.AtomicCache; import java.lang.reflect.InvocationTargetException;
import com.volmit.iris.engine.framework.Engine; import java.lang.reflect.Method;
import com.volmit.iris.util.collection.KList; import java.lang.reflect.Modifier;
import com.volmit.iris.util.collection.KMap; import java.util.*;
import com.volmit.iris.util.hunk.Hunk; import java.util.List;
import com.volmit.iris.util.json.JSONObject; import java.util.concurrent.Executor;
import com.volmit.iris.util.mantle.Mantle; import java.util.concurrent.atomic.AtomicBoolean;
import com.volmit.iris.util.math.Vector3d; import java.util.concurrent.atomic.AtomicInteger;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.io.NBTUtil;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import sun.misc.Unsafe;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -676,103 +676,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().registryOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.registryOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().K.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), RegistrationInfo.BUILT_IN);
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.registryKeySet().forEach(key -> { static void enter(
var value = source.get(key); @Advice.Argument(0) MinecraftServer server,
var info = source.registrationInfo(key).orElse(null); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,42 +1,65 @@
package com.volmit.iris.core.nms.v1_21_R2; package com.volmit.iris.core.nms.v1_21_R2;
import java.awt.Color; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.lang.reflect.Field; import com.volmit.iris.Iris;
import java.lang.reflect.Method; import com.volmit.iris.core.nms.INMSBinding;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.*; import net.minecraft.core.*;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -50,48 +73,32 @@ import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R2.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_21_R2.util.CraftNamespacedKey;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.awt.*;
import com.volmit.iris.Iris; import java.awt.Color;
import com.volmit.iris.core.nms.INMSBinding; import java.lang.reflect.Field;
import com.volmit.iris.engine.data.cache.AtomicCache; import java.lang.reflect.InvocationTargetException;
import com.volmit.iris.engine.framework.Engine; import java.lang.reflect.Method;
import com.volmit.iris.util.collection.KList; import java.lang.reflect.Modifier;
import com.volmit.iris.util.collection.KMap; import java.util.*;
import com.volmit.iris.util.hunk.Hunk; import java.util.List;
import com.volmit.iris.util.json.JSONObject; import java.util.concurrent.Executor;
import com.volmit.iris.util.mantle.Mantle; import java.util.concurrent.atomic.AtomicBoolean;
import com.volmit.iris.util.math.Vector3d; import java.util.concurrent.atomic.AtomicInteger;
import com.volmit.iris.util.matter.MatterBiomeInject;
import com.volmit.iris.util.nbt.mca.NBTWorld;
import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -666,103 +673,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.lookupOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().L.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), RegistrationInfo.BUILT_IN);
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.listElementIds().forEach(key -> { static void enter(
var value = source.getValue(key); @Advice.Argument(0) MinecraftServer server,
var info = source.registrationInfo(key).orElse(null); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,17 +1,18 @@
package com.volmit.iris.core.nms.v1_21_R3; package com.volmit.iris.core.nms.v1_21_R3;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.Mantle;
@ -22,20 +23,24 @@ import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.Registry; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.*; import net.minecraft.core.*;
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
@ -47,13 +52,15 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -67,6 +74,7 @@ import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R3.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_21_R3.util.CraftNamespacedKey;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
@ -74,23 +82,23 @@ import org.jetbrains.annotations.NotNull;
import java.awt.Color; import java.awt.Color;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -665,103 +673,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.lookupOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().L.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), RegistrationInfo.BUILT_IN);
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.listElementIds().forEach(key -> { static void enter(
var value = source.getValue(key); @Advice.Argument(0) MinecraftServer server,
var info = source.registrationInfo(key).orElse(null); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -1,16 +1,17 @@
package com.volmit.iris.core.nms.v1_21_R4; package com.volmit.iris.core.nms.v1_21_R4;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.Mantle;
@ -21,20 +22,24 @@ import com.volmit.iris.util.nbt.mca.palette.*;
import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.nbt.tag.CompoundTag;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import lombok.SneakyThrows; import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.Registry; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.*; import net.minecraft.core.*;
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
@ -46,13 +51,16 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@ -66,6 +74,7 @@ import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
@ -73,22 +82,23 @@ import org.jetbrains.annotations.NotNull;
import java.awt.Color; import java.awt.Color;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData(); private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>(); private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>(); private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>(); private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>(); private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>(); private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final ReentrantLock dataContextLock = new ReentrantLock(true);
private final AtomicCache<Method> byIdRef = new AtomicCache<>(); private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null; private Field biomeStorageCache = null;
@ -663,103 +673,91 @@ public class NMSBinding implements INMSBinding {
} }
@Override @Override
@SneakyThrows public boolean missingDimensionTypes(String... keys) {
public AutoClosing injectLevelStems() { var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); return !Arrays.stream(keys)
.map(key -> ResourceLocation.fromNamespaceAndPath("iris", key))
var server = ((CraftServer) Bukkit.getServer()); .allMatch(type::containsKey);
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
var nmsServer = server.getServer();
var old = nmsServer.worldLoader;
field.setAccessible(true);
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
old.resources(),
old.dataConfiguration(),
old.datapackWorldgen(),
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
)));
return new AutoClosing(() -> {
field.set(nmsServer, old);
dataContextLock.unlock();
});
} }
@Override @Override
@SneakyThrows public boolean injectBukkit() {
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { if (injected.getAndSet(true))
var reg = registry(); return true;
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); try {
field.setAccessible(true); Iris.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class,
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); return true;
var injected = access.lookupOrThrow(Registries.LEVEL_STEM); } catch (Throwable e) {
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg); Iris.error(C.RED + "Failed to inject Bukkit");
var fake = new HashMap<>(old); e.printStackTrace();
fake.put(Registries.LEVEL_STEM, injected); }
field.set(reg, fake); return false;
return new AutoClosing(() -> field.set(reg, old));
} }
@Override public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { if (!(raw instanceof PlatformChunkGenerator gen))
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); throw new IllegalStateException("Generator is not platform chunk generator!");
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return overworld || nether || end; return new LevelStem(dimensionType, chunkGenerator(access));
} }
@Override private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
public void removeCustomDimensions(World world) { var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
((CraftWorld) world).getHandle().L.customDimensions = null;
}
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
var access = registry();
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
var settings = new FlatLevelGeneratorSettings(
Optional.empty(),
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
List.of()
);
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers(); settings.updateLayers();
return new FlatLevelSource(settings);
var source = new FlatLevelSource(settings);
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
if (end) register(fake, dimensions, source, LevelStem.END);
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
} }
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) { private static class ChunkAccessAdvice {
var loc = createIrisKey(key); @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
target.register(key, new LevelStem( static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), return index >= access.getPostProcessing().length;
source }
), RegistrationInfo.BUILT_IN);
} }
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) { private static class ServerLevelAdvice {
if (source == null) return; @Advice.OnMethodEnter
source.listElementIds().forEach(key -> { static void enter(
var value = source.getValue(key); @Advice.Argument(0) MinecraftServer server,
var info = source.registrationInfo(key).orElse(null); @Advice.Argument(3) PrimaryLevelData levelData,
if (value != null && info != null && !target.containsKey(key)) @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
target.register(key, value, info); @Advice.Argument(12) World.Environment env,
}); @Advice.Argument(value = 13) ChunkGenerator gen
} ) {
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
return;
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) { try {
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
levelStem = (LevelStem) bindings.getClass()
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
.invoke(bindings, server.registryAccess(), gen);
levelData.customDimensions = null;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
} }
} }

View File

@ -28,7 +28,7 @@ plugins {
rootProject.name = "Iris" rootProject.name = "Iris"
include(":core") include(":core", ":core:agent")
include( include(
":nms:v1_21_R4", ":nms:v1_21_R4",
":nms:v1_21_R3", ":nms:v1_21_R3",