mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-17 09:43:47 +00:00
commit
304b01d0cf
@ -69,7 +69,7 @@ public class ServerConfigurator {
|
||||
}
|
||||
|
||||
private static void increaseKeepAliveSpigot() throws IOException, InvalidConfigurationException {
|
||||
File spigotConfig = new File("config/spigot.yml");
|
||||
File spigotConfig = new File("spigot.yml");
|
||||
FileConfiguration f = new YamlConfiguration();
|
||||
f.load(spigotConfig);
|
||||
long tt = f.getLong("settings.timeout-time");
|
||||
@ -101,16 +101,27 @@ public class ServerConfigurator {
|
||||
}
|
||||
KList<File> worlds = new KList<>();
|
||||
Bukkit.getServer().getWorlds().forEach(w -> worlds.add(new File(w.getWorldFolder(), "datapacks")));
|
||||
if (worlds.isEmpty()) {
|
||||
worlds.add(new File(getMainWorldFolder(), "datapacks"));
|
||||
}
|
||||
return worlds;
|
||||
}
|
||||
|
||||
private static boolean postVerifyDataPacks(boolean fast) {
|
||||
private static File getMainWorldFolder() {
|
||||
try {
|
||||
Properties prop = new Properties();
|
||||
prop.load(new FileInputStream("server.properties"));
|
||||
String world = prop.getProperty("level-name");
|
||||
File worldFolder = new File(Bukkit.getWorldContainer(), world);
|
||||
File datapacksFolder = new File(worldFolder, "datapacks");
|
||||
return new File(Bukkit.getWorldContainer(), world);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean postVerifyDataPacks(boolean fast) {
|
||||
try {
|
||||
File datapacksFolder = new File(getMainWorldFolder(), "datapacks");
|
||||
File IrisDatapacks = new File(datapacksFolder, "iris");
|
||||
if (!datapacksFolder.exists() || !IrisDatapacks.exists()) {
|
||||
return (true);
|
||||
@ -213,6 +224,10 @@ public class ServerConfigurator {
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
for (File folder : getDatapacksFolder()) {
|
||||
if (INMS.get().loadDatapack(folder)) return;
|
||||
}
|
||||
|
||||
if (allowRestarting) {
|
||||
restart();
|
||||
} else if (INMS.get().supportsDataPacks()) {
|
||||
|
@ -22,6 +22,7 @@ import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisEntity;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
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.nbt.mca.palette.MCABiomeContainer;
|
||||
@ -40,6 +41,8 @@ import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface INMSBinding {
|
||||
boolean hasTile(Location l);
|
||||
|
||||
@ -112,4 +115,6 @@ public interface INMSBinding {
|
||||
Vector3d getBoundingbox(org.bukkit.entity.EntityType entity);
|
||||
|
||||
Entity spawnEntity(Location location, EntityType type, CreatureSpawnEvent.SpawnReason reason);
|
||||
|
||||
boolean loadDatapack(File datapackFolder);
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class NMSBinding1X implements INMSBinding {
|
||||
private static final boolean supportsCustomHeight = testCustomHeight();
|
||||
|
||||
@ -97,6 +99,11 @@ public class NMSBinding1X implements INMSBinding {
|
||||
return location.getWorld().spawnEntity(location, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File datapackFolder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeTile(CompoundTag s, Location newPosition) {
|
||||
|
||||
|
@ -4,15 +4,28 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -531,6 +544,146 @@ public class NMSBinding implements INMSBinding {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File folder) {
|
||||
var data = new File(folder, "iris/data");
|
||||
if (!data.exists() || !data.isDirectory()) return false;
|
||||
FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json");
|
||||
|
||||
var dimensionFolder = new File(data, "minecraft/dimension_type");
|
||||
if (dimensionFolder.exists()) {
|
||||
var files = dimensionFolder.listFiles(jsonFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
modifyDimension(file);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Unable to modify dimension!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory());
|
||||
if (files == null) return false;
|
||||
for (File file : files) {
|
||||
var biome = new File(file, "worldgen/biome");
|
||||
if (!biome.exists()) continue;
|
||||
var biomeFiles = biome.listFiles(jsonFilter);
|
||||
if (biomeFiles == null) continue;
|
||||
for (File biomeFile : biomeFiles) {
|
||||
try {
|
||||
registerBiome(file.getName(), biomeFile);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceLocation from(String namespace, File file) {
|
||||
var name = file.getName();
|
||||
return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
private void registerBiome(String namespace, File file) throws Throwable {
|
||||
var rawRegistry = registry().registry(Registry.BIOME_REGISTRY).orElse(null);
|
||||
var key = ResourceKey.create(Registry.BIOME_REGISTRY, from(namespace, file));
|
||||
if (!(rawRegistry instanceof MappedRegistry<net.minecraft.world.level.biome.Biome> registry))
|
||||
throw new IllegalStateException("The Biome Registry is not a mapped Registry!");
|
||||
if (registry.containsKey(key)) return;
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field holdersField = null;
|
||||
boolean holders = false;
|
||||
for (Field f : MappedRegistry.class.getDeclaredFields()) {
|
||||
if (!f.getGenericType().getTypeName().startsWith("java.util.Map<T, "))
|
||||
continue;
|
||||
holdersField = f;
|
||||
}
|
||||
if (holdersField != null) {
|
||||
holdersField.setAccessible(true);
|
||||
holders = holdersField.get(registry) == null;
|
||||
if (holders) holdersField.set(registry, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
try {
|
||||
var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (biome == null)
|
||||
throw new IllegalStateException("Failed to decode biome " + file.getName());
|
||||
|
||||
registry.createIntrusiveHolder(biome);
|
||||
registry.register(key, biome, Lifecycle.stable());
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
if (holders) {
|
||||
holdersField.set(registry, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyDimension(File file) throws Throwable {
|
||||
var key = ResourceKey.create(Registry.DIMENSION_TYPE_REGISTRY, from("minecraft", file));
|
||||
var rawRegistry = registry().registry(Registry.DIMENSION_TYPE_REGISTRY).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<DimensionType> registry))
|
||||
throw new IllegalStateException("The Dimension Registry is not a mapped Registry!");
|
||||
|
||||
var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key));
|
||||
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);
|
||||
Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName()));
|
||||
lifecyclesField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<DimensionType>) toIdField.get(registry);
|
||||
var byValue = (Map<DimensionType, Holder.Reference<DimensionType>>) byValueField.get(registry);
|
||||
var lifecycles = (Map<DimensionType, Lifecycle>) lifecyclesField.get(registry);
|
||||
|
||||
var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (newValue == null)
|
||||
throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file);
|
||||
|
||||
valueField.set(holder, newValue);
|
||||
toId.put(newValue, toId.removeInt(oldValue));
|
||||
byValue.put(newValue, byValue.remove(oldValue));
|
||||
lifecycles.put(newValue, lifecycles.remove(oldValue));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
|
@ -4,15 +4,28 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -533,6 +546,145 @@ public class NMSBinding implements INMSBinding {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File folder) {
|
||||
var data = new File(folder, "iris/data");
|
||||
if (!data.exists() || !data.isDirectory()) return false;
|
||||
FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json");
|
||||
|
||||
var dimensionFolder = new File(data, "minecraft/dimension_type");
|
||||
if (dimensionFolder.exists()) {
|
||||
var files = dimensionFolder.listFiles(jsonFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
modifyDimension(file);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Unable to modify dimension!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory());
|
||||
if (files == null) return false;
|
||||
for (File file : files) {
|
||||
var biome = new File(file, "worldgen/biome");
|
||||
if (!biome.exists()) continue;
|
||||
var biomeFiles = biome.listFiles(jsonFilter);
|
||||
if (biomeFiles == null) continue;
|
||||
for (File biomeFile : biomeFiles) {
|
||||
try {
|
||||
registerBiome(file.getName(), biomeFile);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceLocation from(String namespace, File file) {
|
||||
var name = file.getName();
|
||||
return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
private void registerBiome(String namespace, File file) throws Throwable {
|
||||
var rawRegistry = registry().registry(Registries.BIOME).orElse(null);
|
||||
var key = ResourceKey.create(Registries.BIOME, from(namespace, file));
|
||||
if (!(rawRegistry instanceof MappedRegistry<net.minecraft.world.level.biome.Biome> registry))
|
||||
throw new IllegalStateException("The Biome Registry is not a mapped Registry!");
|
||||
if (registry.containsKey(key)) return;
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field holdersField = null;
|
||||
boolean holders = false;
|
||||
for (Field f : MappedRegistry.class.getDeclaredFields()) {
|
||||
if (!f.getGenericType().getTypeName().startsWith("java.util.Map<T, "))
|
||||
continue;
|
||||
holdersField = f;
|
||||
}
|
||||
if (holdersField != null) {
|
||||
holdersField.setAccessible(true);
|
||||
holders = holdersField.get(registry) == null;
|
||||
if (holders) holdersField.set(registry, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
try {
|
||||
var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (biome == null)
|
||||
throw new IllegalStateException("Failed to decode biome " + file.getName());
|
||||
|
||||
registry.createIntrusiveHolder(biome);
|
||||
registry.register(key, biome, Lifecycle.stable());
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
if (holders) {
|
||||
holdersField.set(registry, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyDimension(File file) throws Throwable {
|
||||
var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file));
|
||||
var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<DimensionType> registry))
|
||||
throw new IllegalStateException("The Dimension Registry is not a mapped Registry!");
|
||||
|
||||
var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key));
|
||||
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);
|
||||
Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName()));
|
||||
lifecyclesField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<DimensionType>) toIdField.get(registry);
|
||||
var byValue = (Map<DimensionType, Holder.Reference<DimensionType>>) byValueField.get(registry);
|
||||
var lifecycles = (Map<DimensionType, Lifecycle>) lifecyclesField.get(registry);
|
||||
|
||||
var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (newValue == null)
|
||||
throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file);
|
||||
|
||||
valueField.set(holder, newValue);
|
||||
toId.put(newValue, toId.removeInt(oldValue));
|
||||
byValue.put(newValue, byValue.remove(oldValue));
|
||||
lifecycles.put(newValue, lifecycles.remove(oldValue));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
|
@ -4,15 +4,28 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -537,6 +550,145 @@ public class NMSBinding implements INMSBinding {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File folder) {
|
||||
var data = new File(folder, "iris/data");
|
||||
if (!data.exists() || !data.isDirectory()) return false;
|
||||
FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json");
|
||||
|
||||
var dimensionFolder = new File(data, "minecraft/dimension_type");
|
||||
if (dimensionFolder.exists()) {
|
||||
var files = dimensionFolder.listFiles(jsonFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
modifyDimension(file);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Unable to modify dimension!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory());
|
||||
if (files == null) return false;
|
||||
for (File file : files) {
|
||||
var biome = new File(file, "worldgen/biome");
|
||||
if (!biome.exists()) continue;
|
||||
var biomeFiles = biome.listFiles(jsonFilter);
|
||||
if (biomeFiles == null) continue;
|
||||
for (File biomeFile : biomeFiles) {
|
||||
try {
|
||||
registerBiome(file.getName(), biomeFile);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceLocation from(String namespace, File file) {
|
||||
var name = file.getName();
|
||||
return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
private void registerBiome(String namespace, File file) throws Throwable {
|
||||
var rawRegistry = registry().registry(Registries.BIOME).orElse(null);
|
||||
var key = ResourceKey.create(Registries.BIOME, from(namespace, file));
|
||||
if (!(rawRegistry instanceof MappedRegistry<net.minecraft.world.level.biome.Biome> registry))
|
||||
throw new IllegalStateException("The Biome Registry is not a mapped Registry!");
|
||||
if (registry.containsKey(key)) return;
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field holdersField = null;
|
||||
boolean holders = false;
|
||||
for (Field f : MappedRegistry.class.getDeclaredFields()) {
|
||||
if (!f.getGenericType().getTypeName().startsWith("java.util.Map<T, "))
|
||||
continue;
|
||||
holdersField = f;
|
||||
}
|
||||
if (holdersField != null) {
|
||||
holdersField.setAccessible(true);
|
||||
holders = holdersField.get(registry) == null;
|
||||
if (holders) holdersField.set(registry, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
try {
|
||||
var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (biome == null)
|
||||
throw new IllegalStateException("Failed to decode biome " + file.getName());
|
||||
|
||||
registry.createIntrusiveHolder(biome);
|
||||
registry.register(key, biome, Lifecycle.stable());
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
if (holders) {
|
||||
holdersField.set(registry, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyDimension(File file) throws Throwable {
|
||||
var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file));
|
||||
var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<DimensionType> registry))
|
||||
throw new IllegalStateException("The Dimension Registry is not a mapped Registry!");
|
||||
|
||||
var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key));
|
||||
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);
|
||||
Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName()));
|
||||
lifecyclesField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<DimensionType>) toIdField.get(registry);
|
||||
var byValue = (Map<DimensionType, Holder.Reference<DimensionType>>) byValueField.get(registry);
|
||||
var lifecycles = (Map<DimensionType, Lifecycle>) lifecyclesField.get(registry);
|
||||
|
||||
var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (newValue == null)
|
||||
throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file);
|
||||
|
||||
valueField.set(holder, newValue);
|
||||
toId.put(newValue, toId.removeInt(oldValue));
|
||||
byValue.put(newValue, byValue.remove(oldValue));
|
||||
lifecycles.put(newValue, lifecycles.remove(oldValue));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
|
@ -1,6 +1,9 @@
|
||||
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.JsonOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
@ -8,6 +11,7 @@ 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.io.IO;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
import com.volmit.iris.util.mantle.Mantle;
|
||||
import com.volmit.iris.util.math.Vector3d;
|
||||
@ -17,15 +21,19 @@ 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 it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
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;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
@ -35,6 +43,7 @@ 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 org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -60,11 +69,15 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@ -523,6 +536,145 @@ public class NMSBinding implements INMSBinding {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File folder) {
|
||||
var data = new File(folder, "iris/data");
|
||||
if (!data.exists() || !data.isDirectory()) return false;
|
||||
FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json");
|
||||
|
||||
var dimensionFolder = new File(data, "minecraft/dimension_type");
|
||||
if (dimensionFolder.exists()) {
|
||||
var files = dimensionFolder.listFiles(jsonFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
modifyDimension(file);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Unable to modify dimension!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory());
|
||||
if (files == null) return false;
|
||||
for (File file : files) {
|
||||
var biome = new File(file, "worldgen/biome");
|
||||
if (!biome.exists()) continue;
|
||||
var biomeFiles = biome.listFiles(jsonFilter);
|
||||
if (biomeFiles == null) continue;
|
||||
for (File biomeFile : biomeFiles) {
|
||||
try {
|
||||
registerBiome(file.getName(), biomeFile);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceLocation from(String namespace, File file) {
|
||||
var name = file.getName();
|
||||
return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
private void registerBiome(String namespace, File file) throws Throwable {
|
||||
var rawRegistry = registry().registry(Registries.BIOME).orElse(null);
|
||||
var key = ResourceKey.create(Registries.BIOME, from(namespace, file));
|
||||
if (!(rawRegistry instanceof MappedRegistry<net.minecraft.world.level.biome.Biome> registry))
|
||||
throw new IllegalStateException("The Biome Registry is not a mapped Registry!");
|
||||
if (registry.containsKey(key)) return;
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field holdersField = null;
|
||||
boolean holders = false;
|
||||
for (Field f : MappedRegistry.class.getDeclaredFields()) {
|
||||
if (!f.getGenericType().getTypeName().startsWith("java.util.Map<T, "))
|
||||
continue;
|
||||
holdersField = f;
|
||||
}
|
||||
if (holdersField != null) {
|
||||
holdersField.setAccessible(true);
|
||||
holders = holdersField.get(registry) == null;
|
||||
if (holders) holdersField.set(registry, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
try {
|
||||
var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (biome == null)
|
||||
throw new IllegalStateException("Failed to decode biome " + file.getName());
|
||||
|
||||
registry.createIntrusiveHolder(biome);
|
||||
registry.register(key, biome, Lifecycle.stable());
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
if (holders) {
|
||||
holdersField.set(registry, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyDimension(File file) throws Throwable {
|
||||
var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file));
|
||||
var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<DimensionType> registry))
|
||||
throw new IllegalStateException("The Dimension Registry is not a mapped Registry!");
|
||||
|
||||
var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key));
|
||||
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);
|
||||
Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName()));
|
||||
lifecyclesField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<DimensionType>) toIdField.get(registry);
|
||||
var byValue = (Map<DimensionType, Holder.Reference<DimensionType>>) byValueField.get(registry);
|
||||
var lifecycles = (Map<DimensionType, Lifecycle>) lifecyclesField.get(registry);
|
||||
|
||||
var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (newValue == null)
|
||||
throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file);
|
||||
|
||||
valueField.set(holder, newValue);
|
||||
toId.put(newValue, toId.removeInt(oldValue));
|
||||
byValue.put(newValue, byValue.remove(oldValue));
|
||||
lifecycles.put(newValue, lifecycles.remove(oldValue));
|
||||
}
|
||||
|
||||
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 inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
|
||||
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
|
||||
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass();
|
||||
|
@ -4,15 +4,27 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -534,6 +546,146 @@ public class NMSBinding implements INMSBinding {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File folder) {
|
||||
var data = new File(folder, "iris/data");
|
||||
if (!data.exists() || !data.isDirectory()) return false;
|
||||
FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json");
|
||||
|
||||
var dimensionFolder = new File(data, "minecraft/dimension_type");
|
||||
if (dimensionFolder.exists()) {
|
||||
var files = dimensionFolder.listFiles(jsonFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
modifyDimension(file);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Unable to modify dimension!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory());
|
||||
if (files == null) return false;
|
||||
for (File file : files) {
|
||||
var biome = new File(file, "worldgen/biome");
|
||||
if (!biome.exists()) continue;
|
||||
var biomeFiles = biome.listFiles(jsonFilter);
|
||||
if (biomeFiles == null) continue;
|
||||
for (File biomeFile : biomeFiles) {
|
||||
try {
|
||||
registerBiome(file.getName(), biomeFile);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceLocation from(String namespace, File file) {
|
||||
var name = file.getName();
|
||||
return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
private void registerBiome(String namespace, File file) throws Throwable {
|
||||
var rawRegistry = registry().registry(Registries.BIOME).orElse(null);
|
||||
var key = ResourceKey.create(Registries.BIOME, from(namespace, file));
|
||||
if (!(rawRegistry instanceof MappedRegistry<net.minecraft.world.level.biome.Biome> registry))
|
||||
throw new IllegalStateException("The Biome Registry is not a mapped Registry!");
|
||||
if (registry.containsKey(key)) return;
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field holdersField = null;
|
||||
boolean holders = false;
|
||||
for (Field f : MappedRegistry.class.getDeclaredFields()) {
|
||||
if (!f.getGenericType().getTypeName().startsWith("java.util.Map<T, "))
|
||||
continue;
|
||||
holdersField = f;
|
||||
}
|
||||
if (holdersField != null) {
|
||||
holdersField.setAccessible(true);
|
||||
holders = holdersField.get(registry) == null;
|
||||
if (holders) holdersField.set(registry, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
try {
|
||||
var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (biome == null)
|
||||
throw new IllegalStateException("Failed to decode biome " + file.getName());
|
||||
|
||||
registry.createIntrusiveHolder(biome);
|
||||
registry.register(key, biome, Lifecycle.stable());
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
if (holders) {
|
||||
holdersField.set(registry, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyDimension(File file) throws Throwable {
|
||||
var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file));
|
||||
var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<DimensionType> registry))
|
||||
throw new IllegalStateException("The Dimension Registry is not a mapped Registry!");
|
||||
|
||||
var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key));
|
||||
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);
|
||||
Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName()));
|
||||
lifecyclesField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<DimensionType>) toIdField.get(registry);
|
||||
var byValue = (Map<DimensionType, Holder.Reference<DimensionType>>) byValueField.get(registry);
|
||||
var lifecycles = (Map<DimensionType, Lifecycle>) lifecyclesField.get(registry);
|
||||
|
||||
var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (newValue == null)
|
||||
throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file);
|
||||
|
||||
valueField.set(holder, newValue);
|
||||
toId.put(newValue, toId.removeInt(oldValue));
|
||||
byValue.put(newValue, byValue.remove(oldValue));
|
||||
lifecycles.put(newValue, lifecycles.remove(oldValue));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
|
@ -4,15 +4,28 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@ -535,6 +548,145 @@ public class NMSBinding implements INMSBinding {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDatapack(File folder) {
|
||||
var data = new File(folder, "iris/data");
|
||||
if (!data.exists() || !data.isDirectory()) return false;
|
||||
FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json");
|
||||
|
||||
var dimensionFolder = new File(data, "minecraft/dimension_type");
|
||||
if (dimensionFolder.exists()) {
|
||||
var files = dimensionFolder.listFiles(jsonFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
modifyDimension(file);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Unable to modify dimension!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory());
|
||||
if (files == null) return false;
|
||||
for (File file : files) {
|
||||
var biome = new File(file, "worldgen/biome");
|
||||
if (!biome.exists()) continue;
|
||||
var biomeFiles = biome.listFiles(jsonFilter);
|
||||
if (biomeFiles == null) continue;
|
||||
for (File biomeFile : biomeFiles) {
|
||||
try {
|
||||
registerBiome(file.getName(), biomeFile);
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceLocation from(String namespace, File file) {
|
||||
var name = file.getName();
|
||||
return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
private void registerBiome(String namespace, File file) throws Throwable {
|
||||
var rawRegistry = registry().registry(Registries.BIOME).orElse(null);
|
||||
var key = ResourceKey.create(Registries.BIOME, from(namespace, file));
|
||||
if (!(rawRegistry instanceof MappedRegistry<net.minecraft.world.level.biome.Biome> registry))
|
||||
throw new IllegalStateException("The Biome Registry is not a mapped Registry!");
|
||||
if (registry.containsKey(key)) return;
|
||||
Field field = getField(MappedRegistry.class, boolean.class);
|
||||
field.setAccessible(true);
|
||||
boolean frozen = field.getBoolean(registry);
|
||||
field.setBoolean(registry, false);
|
||||
Field holdersField = null;
|
||||
boolean holders = false;
|
||||
for (Field f : MappedRegistry.class.getDeclaredFields()) {
|
||||
if (!f.getGenericType().getTypeName().startsWith("java.util.Map<T, "))
|
||||
continue;
|
||||
holdersField = f;
|
||||
}
|
||||
if (holdersField != null) {
|
||||
holdersField.setAccessible(true);
|
||||
holders = holdersField.get(registry) == null;
|
||||
if (holders) holdersField.set(registry, new IdentityHashMap<>());
|
||||
}
|
||||
|
||||
try {
|
||||
var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (biome == null)
|
||||
throw new IllegalStateException("Failed to decode biome " + file.getName());
|
||||
|
||||
registry.createIntrusiveHolder(biome);
|
||||
registry.register(key, biome, Lifecycle.stable());
|
||||
} finally {
|
||||
field.setBoolean(registry, frozen);
|
||||
if (holders) {
|
||||
holdersField.set(registry, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyDimension(File file) throws Throwable {
|
||||
var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file));
|
||||
var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null);
|
||||
if (!(rawRegistry instanceof MappedRegistry<DimensionType> registry))
|
||||
throw new IllegalStateException("The Dimension Registry is not a mapped Registry!");
|
||||
|
||||
var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key));
|
||||
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);
|
||||
Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName()));
|
||||
lifecyclesField.setAccessible(true);
|
||||
var toId = (Reference2IntMap<DimensionType>) toIdField.get(registry);
|
||||
var byValue = (Map<DimensionType, Holder.Reference<DimensionType>>) byValueField.get(registry);
|
||||
var lifecycles = (Map<DimensionType, Lifecycle>) lifecyclesField.get(registry);
|
||||
|
||||
var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file)))
|
||||
.get().left().map(Pair::getFirst).map(Holder::value).orElse(null);
|
||||
if (newValue == null)
|
||||
throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file);
|
||||
|
||||
valueField.set(holder, newValue);
|
||||
toId.put(newValue, toId.removeInt(oldValue));
|
||||
byValue.put(newValue, byValue.remove(oldValue));
|
||||
lifecycles.put(newValue, lifecycles.remove(oldValue));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user