mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-01 07:25:51 +00:00
add initial 1.21.6 bindings
This commit is contained in:
parent
5d28563b7c
commit
0aee647d05
@ -63,6 +63,7 @@ val color = "truecolor"
|
||||
val errorReporting = false
|
||||
|
||||
val nmsBindings = mapOf(
|
||||
"v1_21_R5" to "1.21.6-R0.1-SNAPSHOT",
|
||||
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
|
||||
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
|
||||
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
|
||||
|
@ -35,7 +35,8 @@ public class INMS {
|
||||
"1.21.2", "v1_21_R2",
|
||||
"1.21.3", "v1_21_R2",
|
||||
"1.21.4", "v1_21_R3",
|
||||
"1.21.5", "v1_21_R4"
|
||||
"1.21.5", "v1_21_R4",
|
||||
"1.21.6", "v1_21_R5"
|
||||
);
|
||||
private static final List<Version> PACKS = List.of(
|
||||
new Version(21, 4, "31020"),
|
||||
|
@ -0,0 +1,169 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R5;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisBiome;
|
||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CustomBiomeSource extends BiomeSource {
|
||||
|
||||
private final long seed;
|
||||
private final Engine engine;
|
||||
private final Registry<Biome> biomeCustomRegistry;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||
private final RNG rng;
|
||||
private final KMap<String, Holder<Biome>> customBiomes;
|
||||
|
||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
||||
this.engine = engine;
|
||||
this.seed = seed;
|
||||
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
|
||||
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
|
||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
||||
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
||||
}
|
||||
|
||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
||||
List<Holder<Biome>> b = new ArrayList<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
if (i.isCustom()) {
|
||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||
b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry
|
||||
.getValue(ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()))).get()).get());
|
||||
}
|
||||
} else {
|
||||
b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private static Object getFor(Class<?> type, Object source) {
|
||||
Object o = fieldFor(type, source);
|
||||
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
|
||||
return invokeFor(type, source);
|
||||
}
|
||||
|
||||
private static Object fieldFor(Class<?> returns, Object in) {
|
||||
return fieldForClass(returns, in.getClass(), in);
|
||||
}
|
||||
|
||||
private static Object invokeFor(Class<?> returns, Object in) {
|
||||
for (Method i : in.getClass().getMethods()) {
|
||||
if (i.getReturnType().equals(returns)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||
return i.invoke(in);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||
for (Field i : sourceType.getDeclaredFields()) {
|
||||
if (i.getType().equals(returnType)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||
return (T) i.get(in);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
||||
return getAllBiomes(
|
||||
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
||||
.lookup(Registries.BIOME).orElse(null),
|
||||
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
|
||||
engine).stream();
|
||||
}
|
||||
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
||||
KMap<String, Holder<Biome>> m = new KMap<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
if (i.isCustom()) {
|
||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||
ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId());
|
||||
Biome biome = customRegistry.getValue(resourceLocation);
|
||||
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
|
||||
if (optionalBiomeKey.isEmpty()) {
|
||||
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
|
||||
continue;
|
||||
}
|
||||
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
|
||||
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(biomeKey);
|
||||
if (optionalReferenceHolder.isEmpty()) {
|
||||
Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName());
|
||||
continue;
|
||||
}
|
||||
m.put(j.getId(), optionalReferenceHolder.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private RegistryAccess registry() {
|
||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BiomeSource> codec() {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||
int m = (y - engine.getMinHeight()) << 2;
|
||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
||||
if (ib.isCustom()) {
|
||||
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
||||
} else {
|
||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
||||
return NMSBinding.biomeToBiomeBase(biomeRegistry, v);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R5;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.ResultLocator;
|
||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.*;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.generator.CustomChunkGenerator;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
addAll(dimension.getJigsawStructures(), placements);
|
||||
for (var region : dimension.getAllRegions(engine)) {
|
||||
addAll(region.getJigsawStructures(), placements);
|
||||
for (var biome : region.getAllBiomes(engine))
|
||||
addAll(biome.getJigsawStructures(), placements);
|
||||
}
|
||||
var stronghold = dimension.getStronghold();
|
||||
if (stronghold != null)
|
||||
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
||||
placements.removeIf(Objects::isNull);
|
||||
|
||||
var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow();
|
||||
for (var s : placements) {
|
||||
try {
|
||||
String raw = s.getStructureKey();
|
||||
if (raw == null) continue;
|
||||
boolean tag = raw.startsWith("#");
|
||||
if (tag) raw = raw.substring(1);
|
||||
|
||||
var location = ResourceLocation.parse(raw);
|
||||
if (!tag) {
|
||||
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = TagKey.create(Registries.STRUCTURE, location);
|
||||
var set = registry.get(key).orElse(null);
|
||||
if (set == null) {
|
||||
Iris.error("Could not find structure tag: " + raw);
|
||||
continue;
|
||||
}
|
||||
for (var holder : set) {
|
||||
var resourceKey = holder.unwrapKey().orElse(null);
|
||||
if (resourceKey == null) continue;
|
||||
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to load structure: " + s.getLoadKey());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
||||
if (placements == null) return;
|
||||
placements.stream()
|
||||
.map(IrisJigsawStructurePlacement::getStructure)
|
||||
.map(engine.getData().getJigsawStructureLoader()::load)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(structures::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
||||
if (holders.size() == 0) return null;
|
||||
if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
|
||||
var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
|
||||
return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
|
||||
}
|
||||
if (engine.getDimension().isDisableExplorerMaps())
|
||||
return null;
|
||||
|
||||
KMap<String, Holder<Structure>> structures = new KMap<>();
|
||||
for (var holder : holders) {
|
||||
if (holder == null) continue;
|
||||
var key = holder.unwrapKey().orElse(null);
|
||||
var set = this.structures.get(key);
|
||||
if (set == null) continue;
|
||||
for (var structure : set) {
|
||||
structures.put(structure, holder);
|
||||
}
|
||||
}
|
||||
if (structures.isEmpty())
|
||||
return null;
|
||||
|
||||
var locator = ResultLocator.locateStructure(structures.keySet())
|
||||
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
||||
if (findUnexplored)
|
||||
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
||||
|
||||
try {
|
||||
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
||||
if (result == null) return null;
|
||||
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
||||
return Pair.of(blockPos, result.obj());
|
||||
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends ChunkGenerator> codec() {
|
||||
return MapCodec.unit(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator getDelegate() {
|
||||
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
||||
return chunkGenerator.getDelegate();
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return delegate.getMinY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return delegate.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) {
|
||||
return delegate.createState(holderlookup, randomstate, i, conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
||||
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
||||
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
||||
return delegate.getSpawnHeight(levelheightaccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenDepth() {
|
||||
return delegate.getGenDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
|
||||
return delegate.getTypeNameForDataFixer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
delegate.validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> holder) {
|
||||
return delegate.getBiomeGenerationSettings(holder);
|
||||
}
|
||||
|
||||
static {
|
||||
Field biomeSource = null;
|
||||
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
||||
if (!field.getType().equals(BiomeSource.class))
|
||||
continue;
|
||||
biomeSource = field;
|
||||
break;
|
||||
}
|
||||
if (biomeSource == null)
|
||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
try {
|
||||
BIOME_SOURCE.set(generator, source);
|
||||
if (generator instanceof CustomChunkGenerator custom)
|
||||
BIOME_SOURCE.set(custom.getDelegate(), source);
|
||||
|
||||
return generator;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,765 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R5;
|
||||
|
||||
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.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 com.volmit.iris.util.scheduling.J;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import lombok.SneakyThrows;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.*;
|
||||
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.tags.TagKey;
|
||||
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.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 org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockStates;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R5.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
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 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;
|
||||
|
||||
private static Object getFor(Class<?> type, Object source) {
|
||||
Object o = fieldFor(type, source);
|
||||
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
|
||||
return invokeFor(type, source);
|
||||
}
|
||||
|
||||
private static Object invokeFor(Class<?> returns, Object in) {
|
||||
for (Method i : in.getClass().getMethods()) {
|
||||
if (i.getReturnType().equals(returns)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||
return i.invoke(in);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object fieldFor(Class<?> returns, Object in) {
|
||||
return fieldForClass(returns, in.getClass(), in);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||
for (Field i : sourceType.getDeclaredFields()) {
|
||||
if (i.getType().equals(returnType)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||
return (T) i.get(in);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
||||
return type.getDeclaredClasses()[ordinal];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTile(Material material) {
|
||||
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTile(Location l) {
|
||||
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public KMap<String, Object> serializeTile(Location location) {
|
||||
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false);
|
||||
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry());
|
||||
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
||||
}
|
||||
|
||||
@Contract(value = "null, _, _ -> null", pure = true)
|
||||
private Object convertFromTag(Tag tag, int depth, int maxDepth) {
|
||||
if (tag == null || depth > maxDepth) return null;
|
||||
return switch (tag) {
|
||||
case CollectionTag collection -> {
|
||||
KList<Object> list = new KList<>();
|
||||
|
||||
for (Object i : collection) {
|
||||
if (i instanceof Tag t)
|
||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
||||
else list.add(i);
|
||||
}
|
||||
yield list;
|
||||
}
|
||||
case net.minecraft.nbt.CompoundTag compound -> {
|
||||
KMap<String, Object> map = new KMap<>();
|
||||
|
||||
for (String key : compound.keySet()) {
|
||||
var child = compound.get(key);
|
||||
if (child == null) continue;
|
||||
var value = convertFromTag(child, depth + 1, maxDepth);
|
||||
if (value == null) continue;
|
||||
map.put(key, value);
|
||||
}
|
||||
yield map;
|
||||
}
|
||||
case NumericTag numeric -> numeric.box();
|
||||
default -> tag.asString().orElse(null);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
J.s(() -> merge(level, blockPos, tag));
|
||||
}
|
||||
|
||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
||||
var blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||
var state = level.getBlockState(blockPos);
|
||||
if (!state.hasBlockEntity())
|
||||
return;
|
||||
|
||||
blockEntity = ((EntityBlock) state.getBlock())
|
||||
.newBlockEntity(blockPos, state);
|
||||
}
|
||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||
accessor.setData(accessor.getData().merge(tag));
|
||||
}
|
||||
|
||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
||||
return switch (object) {
|
||||
case Map<?, ?> map -> {
|
||||
var tag = new net.minecraft.nbt.CompoundTag();
|
||||
for (var i : map.entrySet()) {
|
||||
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
||||
}
|
||||
yield tag;
|
||||
}
|
||||
case List<?> list -> {
|
||||
var tag = new ListTag();
|
||||
for (var i : list) {
|
||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
||||
}
|
||||
yield tag;
|
||||
}
|
||||
case Byte number -> ByteTag.valueOf(number);
|
||||
case Short number -> ShortTag.valueOf(number);
|
||||
case Integer number -> IntTag.valueOf(number);
|
||||
case Long number -> LongTag.valueOf(number);
|
||||
case Float number -> FloatTag.valueOf(number);
|
||||
case Double number -> DoubleTag.valueOf(number);
|
||||
case String string -> StringTag.valueOf(string);
|
||||
default -> EndTag.INSTANCE;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeEntity(Entity location) {
|
||||
return null;// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
||||
return null;// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomHeight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private RegistryAccess registry() {
|
||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||
}
|
||||
|
||||
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
||||
return registry().lookup(Registries.BIOME).orElse(null);
|
||||
}
|
||||
|
||||
private Registry<Block> getBlockRegistry() {
|
||||
return registry().lookup(Registries.BLOCK).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBaseFromId(int id) {
|
||||
return getCustomBiomeRegistry().get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight(World world) {
|
||||
return world.getMinHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomBiomes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrueBiomeBaseId(Object biomeBase) {
|
||||
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTrueBiomeBase(Location location) {
|
||||
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTrueBiomeBaseKey(Location location) {
|
||||
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCustomBiomeBaseFor(String mckey) {
|
||||
return getCustomBiomeRegistry().getValue(ResourceLocation.parse(mckey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
||||
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null);
|
||||
}
|
||||
|
||||
public int getBiomeBaseIdForKey(String key) {
|
||||
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.parse(key)).map(Holder::value).orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyForBiomeBase(Object biomeBase) {
|
||||
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBase(World world, Biome biome) {
|
||||
return biomeToBiomeBase(((CraftWorld) world).getHandle()
|
||||
.registryAccess().lookup(Registries.BIOME).orElse(null), biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBase(Object registry, Biome biome) {
|
||||
Object v = baseBiomeCache.get(biome);
|
||||
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
//noinspection unchecked
|
||||
v = biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
||||
if (v == null) {
|
||||
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
||||
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
||||
// So, we will just return the ID that the plains biome returns instead.
|
||||
//noinspection unchecked
|
||||
return biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
||||
}
|
||||
baseBiomeCache.put(biome, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<Biome> getBiomes() {
|
||||
return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBukkit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBiomeId(Biome biome) {
|
||||
for (World i : Bukkit.getWorlds()) {
|
||||
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
||||
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null);
|
||||
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
||||
}
|
||||
}
|
||||
|
||||
return biome.ordinal();
|
||||
}
|
||||
|
||||
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
||||
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
||||
return getCustomBiomeRegistry().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
||||
return getCustomBiomeRegistry().getId(paramT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
||||
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
||||
return new MCABiomeContainer() {
|
||||
@Override
|
||||
public int[] getData() {
|
||||
return base.writeBiomes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int y, int z, int id) {
|
||||
base.setBiome(x, y, z, biomeMapping.byId(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBiome(int x, int y, int z) {
|
||||
return biomeMapping.getId(base.getBiome(x, y, z));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countCustomBiomes() {
|
||||
AtomicInteger a = new AtomicInteger(0);
|
||||
|
||||
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
||||
if (i.getNamespace().equals("minecraft")) {
|
||||
return;
|
||||
}
|
||||
|
||||
a.incrementAndGet();
|
||||
Iris.debug("Custom Biome: " + i);
|
||||
});
|
||||
|
||||
return a.get();
|
||||
}
|
||||
|
||||
public boolean supportsDataPacks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
||||
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
||||
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
||||
c.markUnsaved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
||||
try {
|
||||
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
||||
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
||||
s.setBiome(x, y, z, biome);
|
||||
} catch (IllegalAccessException e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Field getFieldForBiomeStorage(Object storage) {
|
||||
Field f = biomeStorageCache;
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
try {
|
||||
f = storage.getClass().getDeclaredField("biome");
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.error(storage.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
biomeStorageCache = f;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCAPaletteAccess createPalette() {
|
||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
||||
Field cf = IdMapper.class.getDeclaredField("tToId");
|
||||
Field df = IdMapper.class.getDeclaredField("idToT");
|
||||
Field bf = IdMapper.class.getDeclaredField("nextId");
|
||||
cf.setAccessible(true);
|
||||
df.setAccessible(true);
|
||||
bf.setAccessible(true);
|
||||
IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
||||
int b = bf.getInt(blockData);
|
||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
||||
return new MCAIdMapper<BlockState>(c, d, b);
|
||||
});
|
||||
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
||||
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||
((CraftBlockData) AIR).getState());
|
||||
return new MCAWrappedPalettedContainer<>(container,
|
||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
||||
ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL);
|
||||
AtomicInteger c = new AtomicInteger();
|
||||
AtomicInteger r = new AtomicInteger();
|
||||
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
||||
if (b != null) {
|
||||
if (b.isCustom()) {
|
||||
chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get());
|
||||
c.getAndIncrement();
|
||||
} else {
|
||||
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
||||
r.getAndIncrement();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
||||
if (customNbt != null && !customNbt.isEmpty()) {
|
||||
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
||||
|
||||
try {
|
||||
net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString());
|
||||
tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe());
|
||||
s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||
} catch (CommandSyntaxException var5) {
|
||||
throw new IllegalArgumentException(var5);
|
||||
}
|
||||
|
||||
return CraftItemStack.asBukkitCopy(s);
|
||||
} else {
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
|
||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||
worldGenContextField.setAccessible(true);
|
||||
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||
|
||||
var newContext = new WorldGenContext(
|
||||
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
|
||||
|
||||
worldGenContextField.set(chunkMap, newContext);
|
||||
}
|
||||
|
||||
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
||||
Field[] fields = EntityType.class.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
||||
try {
|
||||
EntityType entityType = (EntityType) field.get(null);
|
||||
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
||||
Vector<Float> v1 = new Vector<>();
|
||||
v1.add(entityType.getHeight());
|
||||
entityType.getDimensions();
|
||||
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
||||
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
||||
return box;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
Iris.error("Unable to get entity dimensions!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getBiomeColor(Location location, BiomeColor type) {
|
||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
var biome = holder.value();
|
||||
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
||||
|
||||
int rgba = switch (type) {
|
||||
case FOG -> biome.getFogColor();
|
||||
case WATER -> biome.getWaterColor();
|
||||
case WATER_FOG -> biome.getWaterFogColor();
|
||||
case SKY -> biome.getSkyColor();
|
||||
case FOLIAGE -> biome.getFoliageColor();
|
||||
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
||||
};
|
||||
if (rgba == 0) {
|
||||
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
||||
return null;
|
||||
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
|
||||
return null;
|
||||
}
|
||||
return new Color(rgba, true);
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getType().equals(fieldType))
|
||||
return f;
|
||||
}
|
||||
throw new NoSuchFieldException(fieldType.getName());
|
||||
} catch (NoSuchFieldException var4) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass == null) {
|
||||
throw var4;
|
||||
} else {
|
||||
return getField(superClass, fieldType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
|
||||
return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnChunkCount(World world) {
|
||||
var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS))
|
||||
.orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS));
|
||||
if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!");
|
||||
return (int) Math.pow(2 * radius + 1, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<String> getStructureKeys() {
|
||||
KList<String> keys = new KList<>();
|
||||
|
||||
var registry = registry().lookup(Registries.STRUCTURE).orElse(null);
|
||||
if (registry == null) return keys;
|
||||
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
|
||||
registry.getTags()
|
||||
.map(HolderSet.Named::key)
|
||||
.map(TagKey::location)
|
||||
.map(ResourceLocation::toString)
|
||||
.map(s -> "#" + s)
|
||||
.forEach(keys::add);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
@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();
|
||||
});
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCustomDimensions(World world) {
|
||||
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||
}
|
||||
|
||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||
var access = registry();
|
||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||
|
||||
var settings = new FlatLevelGeneratorSettings(
|
||||
Optional.empty(),
|
||||
access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID),
|
||||
List.of()
|
||||
);
|
||||
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();
|
||||
}
|
||||
|
||||
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 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 ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ rootProject.name = "Iris"
|
||||
|
||||
include(":core")
|
||||
include(
|
||||
":nms:v1_21_R5",
|
||||
":nms:v1_21_R4",
|
||||
":nms:v1_21_R3",
|
||||
":nms:v1_21_R2",
|
||||
|
Loading…
x
Reference in New Issue
Block a user