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 {
compileOnly(project(":core"))
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("com.mojang.eula.agree", true)
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(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) })
from(project(":core:agent").tasks.jar.flatMap { it.archiveFile })
archiveFileName.set("Iris-${project.version}.jar")
}
@ -168,10 +171,6 @@ fun exec(vararg command: Any) {
p.waitFor()
}
dependencies {
implementation(project(":core"))
}
configurations.configureEach {
resolutionStrategy.cacheChangingModulesFor(60, "minutes")
resolutionStrategy.cacheDynamicVersionsFor(60, "minutes")
@ -193,6 +192,7 @@ allprojects {
maven("https://repo.mineinabyss.com/releases")
maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/")
maven("https://repo.nexomc.com/releases/")
maven("https://nexus.phoenixdevt.fr/repository/maven-public/")
}
dependencies {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,11 +19,12 @@
package com.volmit.iris.engine.platform;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.AutoClosing;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.IrisEngine;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.data.chunk.TerrainChunk;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget;
@ -61,7 +62,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
@ -87,6 +87,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
private final AtomicCache<EngineTarget> targetCache = new AtomicCache<>();
private volatile Engine engine;
private volatile Looper hotloader;
private volatile StudioMode lastMode;
@ -113,36 +114,35 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldInit(WorldInitEvent event) {
try {
if (initialized || !world.name().equals(event.getWorld().getName()))
return;
AutoClosing.closeContext();
INMS.get().removeCustomDimensions(event.getWorld());
world.setRawWorldSeed(event.getWorld().getSeed());
Engine engine = getEngine(event.getWorld());
if (engine == null) {
Iris.warn("Failed to get Engine!");
if (initialize(event.getWorld())) return;
Iris.warn("Failed to get Engine for " + event.getWorld().getName() + " re-trying...");
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();
}
if (!initialize(event.getWorld())) {
Iris.error("Failed to get Engine for " + event.getWorld().getName() + "!");
}
}, 10);
} else {
INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld());
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
spawnChunks.complete(INMS.get().getSpawnChunkCount(event.getWorld()));
initialized = true;
}
private boolean initialize(World world) {
Engine engine = getEngine(world);
if (engine == null) return false;
try {
INMS.get().inject(world.getSeed(), engine, world);
Iris.info("Injected Iris Biome Source into " + world.getName());
} catch (Throwable e) {
Iris.reportError(e);
Iris.error("Failed to inject biome source into " + world.getName());
e.printStackTrace();
}
spawnChunks.complete(INMS.get().getSpawnChunkCount(world));
Iris.instance.unregisterListener(this);
initialized = true;
IrisWorlds.get().put(world.getName(), dimensionKey);
return true;
}
@Nullable
@ -160,6 +160,18 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
private void setupEngine() {
lastMode = StudioMode.NORMAL;
engine = new IrisEngine(getTarget(), studio);
populators.clear();
targetCache.reset();
}
@NotNull
@Override
public EngineTarget getTarget() {
if (engine != null) return engine.getTarget();
return targetCache.aquire(() -> {
IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
@ -188,9 +200,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
}
lastMode = StudioMode.NORMAL;
engine = new IrisEngine(new EngineTarget(world, dimension, data), studio);
populators.clear();
return new EngineTarget(world, dimension, data);
});
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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