diff --git a/src/main/java/com/volmit/iris/core/nms/v19_2/CustomBiomeSource.java b/src/main/java/com/volmit/iris/core/nms/v19_2/CustomBiomeSource.java new file mode 100644 index 000000000..0c295e79b --- /dev/null +++ b/src/main/java/com/volmit/iris/core/nms/v19_2/CustomBiomeSource.java @@ -0,0 +1,150 @@ +package com.volmit.iris.core.nms.v19_2; + +import com.mojang.serialization.Codec; +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.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_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class CustomBiomeSource extends BiomeSource { + private final long seed; + private final Engine engine; + private final Registry biomeCustomRegistry; + private final Registry biomeRegistry; + private final AtomicCache registryAccess = new AtomicCache<>(); + private final RNG rng; + private final KMap> customBiomes; + + public CustomBiomeSource(long seed, Engine engine, World world) { + super(getAllBiomes( + ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) + .registry(Registry.BIOME_REGISTRY).orElse(null), + ((CraftWorld) world).getHandle().registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null), + engine)); + this.engine = engine; + this.seed = seed; + this.biomeCustomRegistry = registry().registry(Registry.BIOME_REGISTRY).orElse(null); + this.biomeRegistry = ((CraftWorld) world).getHandle().registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null); + this.rng = new RNG(engine.getSeedManager().getBiome()); + this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine); + } + + private KMap> fillCustomBiomes(Registry customRegistry, Engine engine) { + KMap> m = new KMap<>(); + + for(IrisBiome i : engine.getAllBiomes()) { + if(i.isCustom()) { + for(IrisBiomeCustom j : i.getCustomDerivitives()) { + m.put(j.getId(), customRegistry.getHolder(customRegistry.getResourceKey(customRegistry + .get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get()); + } + } + } + + return m; + } + + private static List> getAllBiomes(Registry customRegistry, Registry registry, Engine engine) { + List> b = new ArrayList<>(); + + for(IrisBiome i : engine.getAllBiomes()) { + if(i.isCustom()) { + for(IrisBiomeCustom j : i.getCustomDerivitives()) { + b.add(customRegistry.getHolder(customRegistry.getResourceKey(customRegistry + .get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get()); + } + } else { + b.add(CraftBlock.biomeToBiomeBase(registry, i.getVanillaDerivative())); + } + } + + return b; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + 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 fieldForClass(Class 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 Codec codec() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { + IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x, z); + if(ib.isCustom()) { + return customBiomes.get(ib.getCustomBiome(rng, x, 0, z).getId()); + } else { + org.bukkit.block.Biome v = ib.getSkyBiome(rng, x, 0, z); + return CraftBlock.biomeToBiomeBase(biomeRegistry, v); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/core/nms/v19_2/NMSBinding19_2.java b/src/main/java/com/volmit/iris/core/nms/v19_2/NMSBinding19_2.java index 2243292e8..3d46e42eb 100644 --- a/src/main/java/com/volmit/iris/core/nms/v19_2/NMSBinding19_2.java +++ b/src/main/java/com/volmit/iris/core/nms/v19_2/NMSBinding19_2.java @@ -164,23 +164,7 @@ public class NMSBinding19_2 implements INMSBinding { @Override public Object getBiomeBaseFromId(int id) { - try { - return byIdRef.aquire(() -> { - for(Method i : IdMap.class.getDeclaredMethods()) { - if(i.getParameterCount() == 1 && i.getParameterTypes()[0].equals(int.class)) { - Iris.info("[NMS] Found byId method in " + IdMap.class.getSimpleName() + "." + i.getName() + "(int) => " + Biome.class.getSimpleName()); - return i; - } - } - - Iris.error("Cannot find byId method!"); - return null; - }).invoke(getCustomBiomeRegistry(), id); - } catch(IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); - } - - return null; + return getCustomBiomeRegistry().getHolder(id); } @Override @@ -228,7 +212,8 @@ public class NMSBinding19_2 implements INMSBinding { @Override public Object getBiomeBase(World world, Biome biome) { - return getBiomeBase(((CraftWorld) world).getHandle().registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null), biome); + return org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock.biomeToBiomeBase(((CraftWorld) world).getHandle() + .registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null), biome); } @Override @@ -412,7 +397,7 @@ public class NMSBinding19_2 implements INMSBinding { mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x,y,z,b) -> { if(b != null) { if(b.isCustom()) { - chunk.setBiome(x, y, z, (Holder) getBiomeBaseFromId(b.getBiomeId())); + chunk.setBiome(x, y, z, getCustomBiomeRegistry().getHolder(b.getBiomeId()).get()); c.getAndIncrement(); } @@ -422,8 +407,6 @@ public class NMSBinding19_2 implements INMSBinding { } } }); - - Iris.info("Injected " + c.get() + " custom biomes and " + r.get() + " vanilla biomes into chunk " + e.getX() + "," + e.getZ()); } private static Object getFor(Class type, Object source) { diff --git a/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index 3792052bd..3f39742e0 100644 --- a/src/main/java/com/volmit/iris/engine/IrisWorldManager.java +++ b/src/main/java/com/volmit/iris/engine/IrisWorldManager.java @@ -480,7 +480,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { getEngine().cleanupMantleChunk(e.getX(), e.getZ()); if(generated) { - INMS.get().injectBiomesFromMantle(e, getMantle()); + //INMS.get().injectBiomesFromMantle(e, getMantle()); } } diff --git a/src/main/java/com/volmit/iris/engine/actuator/IrisBiomeActuator.java b/src/main/java/com/volmit/iris/engine/actuator/IrisBiomeActuator.java index c2f54ac56..7530f61c2 100644 --- a/src/main/java/com/volmit/iris/engine/actuator/IrisBiomeActuator.java +++ b/src/main/java/com/volmit/iris/engine/actuator/IrisBiomeActuator.java @@ -18,9 +18,7 @@ package com.volmit.iris.engine.actuator; -import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.engine.data.chunk.TerrainChunk; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineAssignedActuator; import com.volmit.iris.engine.object.IrisBiome; @@ -28,17 +26,12 @@ import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder; -import com.volmit.iris.util.hunk.view.BiomeGridHunkView; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.MatterBiomeInject; import com.volmit.iris.util.matter.slices.BiomeInjectMatter; -import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch; -import io.papermc.lib.PaperLib; import org.bukkit.block.Biome; -import org.bukkit.generator.ChunkGenerator; public class IrisBiomeActuator extends EngineAssignedActuator { private final RNG rng; @@ -52,8 +45,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { @BlockCoordinates @Override public void onActuate(int x, int z, Hunk h, boolean multicore, ChunkContext context) { - try - { + try { PrecisionStopwatch p = PrecisionStopwatch.start(); int m = 0; @@ -61,7 +53,6 @@ public class IrisBiomeActuator extends EngineAssignedActuator { IrisBiome ib; for(int zf = 0; zf < h.getDepth(); zf++) { ib = context.getBiome().get(xf, zf); - int maxHeight = (int) (getComplex().getFluidHeight() + ib.getMaxWithObjectHeight(getData())); MatterBiomeInject matter = null; if(ib.isCustom()) { @@ -72,21 +63,13 @@ public class IrisBiomeActuator extends EngineAssignedActuator { matter = BiomeInjectMatter.get(v); } - for(int i = 0; i < maxHeight; i++) { - getEngine().getMantle().getMantle().set(x, i, z, matter); - m++; - } - + getEngine().getMantle().getMantle().set(x + xf, 0, z + zf, matter); + m++; } } getEngine().getMetrics().getBiome().put(p.getMilliseconds()); - Iris.info("Biome Actuator: " + p.getMilliseconds() + "ms"); - Iris.info("Mantle: " + m + " blocks"); - } - - catch(Throwable e) - { + } catch(Throwable e) { e.printStackTrace(); } } diff --git a/src/main/java/com/volmit/iris/engine/mode/ModeOverworld.java b/src/main/java/com/volmit/iris/engine/mode/ModeOverworld.java index bd19cf06a..ab4b0bbb4 100644 --- a/src/main/java/com/volmit/iris/engine/mode/ModeOverworld.java +++ b/src/main/java/com/volmit/iris/engine/mode/ModeOverworld.java @@ -52,7 +52,6 @@ public class ModeOverworld extends IrisEngineMode implements EngineMode { EngineStage sPerfection = (x, z, k, p, m, c) -> perfection.modify(x, z, k, m, c); registerStage(burst( - sBiome, sGenMatter, sTerrain )); diff --git a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 33a800179..7684de59a 100644 --- a/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -20,6 +20,7 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.v19_2.CustomBiomeSource; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.IrisEngine; import com.volmit.iris.engine.data.chunk.TerrainChunk; @@ -44,6 +45,10 @@ import com.volmit.iris.util.stream.utility.ProfiledStream; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Setter; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.world.level.WorldGenLevel; +import org.apache.commons.lang3.reflect.FieldUtils; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.HeightMap; @@ -51,16 +56,26 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.generator.CustomChunkGenerator; +import org.bukkit.craftbukkit.v1_19_R1.generator.InternalChunkGenerator; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldInitEvent; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.WorldInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import sun.misc.Unsafe; import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.List; import java.util.Random; import java.util.concurrent.Semaphore; @@ -71,7 +86,7 @@ import java.util.function.Consumer; @EqualsAndHashCode(callSuper = true) @Data -public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator { +public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator, Listener { private static final int LOAD_LOCKS = Runtime.getRuntime().availableProcessors() * 4; private final Semaphore loadLock; private final IrisWorld world; @@ -102,6 +117,43 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun this.dataLocation = dataLocation; this.dimensionKey = dimensionKey; this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> hotload()); + Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); + } + + @EventHandler + public void onWorldInit(WorldInitEvent event) { + try { + ServerLevel serverLevel = ((CraftWorld)event.getWorld()).getHandle(); + Engine engine = getEngine(event.getWorld()); + Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); + Field biomeSource = getField(clazz, "c"); + biomeSource.setAccessible(true); + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe) unsafeField.get(null); + CustomBiomeSource customBiomeSource = new CustomBiomeSource(event.getWorld().getSeed(), engine, event.getWorld()); + unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); + biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); + } + + catch(Throwable e) { + e.printStackTrace(); + } + } + + private static Field getField(Class clazz, String fieldName) + throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw e; + } else { + return getField(superClass, fieldName); + } + } } private void setupEngine() { @@ -236,7 +288,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun lock.unlock(); - return engine; }