mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-30 04:29:05 +00:00
add missing methods to 1.21 bindings
This commit is contained in:
@@ -1,26 +1,48 @@
|
||||
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.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.RandomSequences;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
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.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@@ -34,6 +56,7 @@ 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.NotNull;
|
||||
@@ -56,10 +79,6 @@ 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.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.NbtIo;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
@@ -581,4 +600,203 @@ public class NMSBinding implements INMSBinding {
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1205;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerDimension(String name, IrisDimension dimension) {
|
||||
var registry = registry(Registries.DIMENSION_TYPE);
|
||||
var baseLocation = switch (dimension.getEnvironment()) {
|
||||
case NORMAL -> ResourceLocation.fromNamespaceAndPath("minecraft", "overworld");
|
||||
case NETHER -> ResourceLocation.fromNamespaceAndPath("minecraft", "the_nether");
|
||||
case THE_END -> ResourceLocation.fromNamespaceAndPath("minecraft", "the_end");
|
||||
case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension");
|
||||
};
|
||||
var base = registry.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, baseLocation)).orElse(null);
|
||||
if (base == null) return false;
|
||||
var json = encode(DimensionType.CODEC, base).orElse(null);
|
||||
if (json == null) return false;
|
||||
var object = json.getAsJsonObject();
|
||||
var height = dimension.getDimensionHeight();
|
||||
object.addProperty("min_y", height.getMin());
|
||||
object.addProperty("height", height.getMax() - height.getMin());
|
||||
object.addProperty("logical_height", dimension.getLogicalHeight());
|
||||
var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null);
|
||||
if (value == null) return false;
|
||||
return register(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", name), value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBiome(String dimensionId, IrisBiomeCustom biome, boolean replace) {
|
||||
var biomeBase = decode(net.minecraft.world.level.biome.Biome.CODEC, biome.generateJson()).map(Holder::value).orElse(null);
|
||||
if (biomeBase == null) return false;
|
||||
return register(Registries.BIOME, ResourceLocation.fromNamespaceAndPath(dimensionId, biome.getId()), biomeBase, replace);
|
||||
}
|
||||
|
||||
private <T> Optional<T> decode(Codec<T> codec, String json) {
|
||||
return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).result().map(Pair::getFirst);
|
||||
}
|
||||
|
||||
private <T> Optional<JsonElement> encode(Codec<T> codec, T value) {
|
||||
return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result();
|
||||
}
|
||||
|
||||
private <T> boolean register(ResourceKey<Registry<T>> registryKey, ResourceLocation location, T value, boolean replace) {
|
||||
Preconditions.checkArgument(registryKey != null, "The registry cannot be null!");
|
||||
Preconditions.checkArgument(location != null, "The location cannot be null!");
|
||||
Preconditions.checkArgument(value != null, "The value cannot be null!");
|
||||
var registry = registry(registryKey);
|
||||
var key = ResourceKey.create(registryKey, location);
|
||||
try {
|
||||
if (registry.containsKey(key)) {
|
||||
if (!replace) return false;
|
||||
return replace(registryKey, location, value);
|
||||
}
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field valueField = getField(Holder.Reference.class, "T");
|
||||
valueField.setAccessible(true);
|
||||
|
||||
try {
|
||||
var holder = registry.register(key, value, RegistrationInfo.BUILT_IN);
|
||||
if (frozen) valueField.set(holder, value);
|
||||
return true;
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> boolean replace(ResourceKey<Registry<T>> registryKey, ResourceLocation location, T value) {
|
||||
Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!");
|
||||
Preconditions.checkArgument(location != null, "The location cannot be null!");
|
||||
Preconditions.checkArgument(value != null, "The value cannot be null!");
|
||||
var registry = registry(registryKey);
|
||||
var key = ResourceKey.create(registryKey, location);
|
||||
try {
|
||||
var holder = registry.getHolder(key).orElse(null);
|
||||
if (holder == null) return false;
|
||||
var oldValue = holder.value();
|
||||
Field valueField = getField(Holder.Reference.class, "T");
|
||||
valueField.setAccessible(true);
|
||||
Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T"));
|
||||
toIdField.setAccessible(true);
|
||||
Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T")));
|
||||
byValueField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<T>) toIdField.get(registry);
|
||||
var byValue = (Map<T, Holder.Reference<T>>) byValueField.get(registry);
|
||||
|
||||
valueField.set(holder, value);
|
||||
toId.put(value, toId.removeInt(oldValue));
|
||||
byValue.put(value, byValue.remove(oldValue));
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> MappedRegistry<T> registry(ResourceKey<Registry<T>> registryKey) {
|
||||
var rawRegistry = registry().registry(registryKey).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<T> registry))
|
||||
throw new IllegalStateException("The Registry is not a mapped Registry!");
|
||||
return registry;
|
||||
}
|
||||
|
||||
private static String buildType(Class<?> clazz, String... parameterTypes) {
|
||||
if (parameterTypes.length == 0) return clazz.getName();
|
||||
var builder = new StringBuilder(clazz.getName())
|
||||
.append("<");
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
builder.append(parameterTypes[i]).append(parameterTypes.length - 1 == i ? ">" : ", ");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, String type) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getGenericType().getTypeName().equals(type))
|
||||
return f;
|
||||
}
|
||||
throw new NoSuchFieldException(type);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass == null) throw e;
|
||||
return getField(superClass, type);
|
||||
}
|
||||
}
|
||||
|
||||
public void injectBukkit() {
|
||||
try {
|
||||
Iris.info("Injecting Bukkit");
|
||||
new ByteBuddy()
|
||||
.redefine(CraftServer.class)
|
||||
.visit(Advice.to(CraftServerAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(WorldCreator.class))))
|
||||
.make()
|
||||
.load(CraftServer.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
|
||||
new ByteBuddy()
|
||||
.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(), ClassReloadingStrategy.fromInstalledAgent());
|
||||
Iris.info("Injected Bukkit Successfully!");
|
||||
} catch (Exception e) {
|
||||
Iris.info(C.RED + "Failed to Inject Bukkit!");
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ServerLevelAdvice {
|
||||
@Advice.OnMethodEnter
|
||||
static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey<Level> key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) {
|
||||
File iris = new File(access.levelDirectory.path().toFile(), "iris");
|
||||
if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return;
|
||||
ResourceKey<DimensionType> typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()));
|
||||
RegistryAccess registryAccess = server.registryAccess();
|
||||
Registry<DimensionType> registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null);
|
||||
if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey);
|
||||
Holder<DimensionType> holder = registry.getHolder(typeKey).orElse(null);
|
||||
if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey);
|
||||
levelStem = new LevelStem(holder, levelStem.generator());
|
||||
}
|
||||
}
|
||||
|
||||
private static class CraftServerAdvice {
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||
static boolean enter(@Advice.This CraftServer self, @Advice.Argument(0) WorldCreator creator) {
|
||||
File isIrisWorld = new File(self.getWorldContainer(), creator.name() + "/iris");
|
||||
boolean isFromIris = false;
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement stack : stackTrace) {
|
||||
if (stack.getClassName().contains("Iris")) {
|
||||
isFromIris = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isIrisWorld.exists() && !isFromIris) {
|
||||
var logger = Logger.getLogger("Iris");
|
||||
logger.warning("detected another Plugin trying to load " + creator.name() + ". This is not supported and will be ignored.");
|
||||
|
||||
if (System.getProperty("iris.debug", "false").equals("true")) {
|
||||
new RuntimeException().printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Advice.OnMethodExit
|
||||
static void exit(@Advice.Enter boolean bool, @Advice.Return(readOnly = false) World returned) {
|
||||
if (bool) {
|
||||
returned = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user