diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java index 357c262c8..843cf8430 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java @@ -42,28 +42,15 @@ public class NMSBiomeInjector { private static final Logger LOGGER = LoggerFactory.getLogger(NMSBiomeInjector.class); private static final Map> terraBiomeMap = new HashMap<>(); - public static IRegistryWritable getBiomeRegistry() { - CraftServer craftserver = (CraftServer) Bukkit.getServer(); - DedicatedServer dedicatedserver = craftserver.getServer(); - - return (IRegistryWritable) dedicatedserver - .aU() // getRegistryManager - .b( // getRegistry - IRegistry.aP // biome registry key - ); - } public static void registerBiomes(ConfigRegistry configRegistry) { - - try { LOGGER.info("Hacking biome registry..."); - IRegistryWritable biomeRegistry = getBiomeRegistry(); + IRegistryWritable biomeRegistry = (IRegistryWritable) Registries.biomeRegistry(); Field frozen = RegistryMaterials.class.getDeclaredField("bL"); // registry frozen field frozen.setAccessible(true); frozen.set(biomeRegistry, false); - configRegistry.forEach(pack -> pack.getRegistry(Biome.class).forEach((key, biome) -> { try { BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome(); @@ -90,53 +77,48 @@ public class NMSBiomeInjector { frozen.set(biomeRegistry, true); // freeze registry again :) + LOGGER.info("Doing tag garbage...."); + Map, List>> collect = biomeRegistry + .g() // streamKeysAndEntries + .collect(HashMap::new, + (map, pair) -> + map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().a().toList())), + HashMap::putAll); + + terraBiomeMap + .forEach((vb, terraBiomes) -> + getEntry(biomeRegistry, vb) + .ifPresentOrElse( + vanilla -> terraBiomes + .forEach(tb -> getEntry(biomeRegistry, tb) + .ifPresentOrElse( + terra -> { + LOGGER.debug(vanilla.e().orElseThrow().a() + + " (vanilla for " + + terra.e().orElseThrow().a() + + ": " + + vanilla.c().toList()); + + vanilla.c() + .forEach( + tag -> collect + .computeIfAbsent(tag, + t -> new ArrayList<>()) + .add(terra)); + }, + () -> LOGGER.error( + "No such biome: {}", + tb))), + () -> LOGGER.error("No vanilla biome: {}", vb))); + + biomeRegistry.k(); // clearTags + biomeRegistry.a(ImmutableMap.copyOf(collect)); // populateTags } catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException exception) { throw new RuntimeException(exception); } } - public static void injectTags() { - LOGGER.info("Doing tag garbage...."); - IRegistryWritable biomeRegistry = getBiomeRegistry(); - Map, List>> collect = biomeRegistry - .g() // streamKeysAndEntries - .collect(HashMap::new, - (map, pair) -> - map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().a().toList())), - HashMap::putAll); - - terraBiomeMap - .forEach((vb, terraBiomes) -> - getEntry(biomeRegistry, vb) - .ifPresentOrElse( - vanilla -> terraBiomes - .forEach(tb -> getEntry(biomeRegistry, tb) - .ifPresentOrElse( - terra -> { - LOGGER.debug(vanilla.e().orElseThrow().a() + - " (vanilla for " + - terra.e().orElseThrow().a() + - ": " + - vanilla.c().toList()); - - vanilla.c() - .forEach( - tag -> collect - .computeIfAbsent(tag, - t -> new ArrayList<>()) - .add(terra)); - }, - () -> LOGGER.error( - "No such biome: {}", - tb))), - () -> LOGGER.error("No vanilla biome: {}", vb))); - - biomeRegistry.k(); // clearTags - biomeRegistry.a(ImmutableMap.copyOf(collect)); // populateTags - - } - public static Optional> getEntry(IRegistry registry, MinecraftKey identifier) { return registry.b(identifier) .flatMap(registry::c) diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java index 017c97045..9105caa8d 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java @@ -1,13 +1,20 @@ package com.dfsek.terra.bukkit.nms; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.core.BlockPosition; +import net.minecraft.core.Holder; import net.minecraft.core.IRegistry; +import net.minecraft.core.IRegistryCustom; +import net.minecraft.core.SectionPosition; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.RegionLimitedWorldAccess; import net.minecraft.world.level.BlockColumn; +import net.minecraft.world.level.ChunkCoordIntPair; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.biome.BiomeBase; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.Climate; import net.minecraft.world.level.biome.Climate.Sampler; @@ -19,21 +26,35 @@ import net.minecraft.world.level.levelgen.HeightMap; import net.minecraft.world.level.levelgen.WorldGenStage; import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; +import net.minecraft.world.level.levelgen.structure.templatesystem.DefinedStructureManager; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_18_R2.CraftServer; import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Random; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.stream.Collectors; import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.util.generic.Lazy; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.info.WorldProperties; public class NMSChunkGeneratorDelegate extends ChunkGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class); private final NMSBiomeProvider biomeSource; private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate; @@ -42,20 +63,12 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { private final long seed; - public static IRegistry getStructureRegistry() { - CraftServer craftserver = (CraftServer) Bukkit.getServer(); - DedicatedServer dedicatedserver = craftserver.getServer(); - - return dedicatedserver - .aU() // getRegistryManager - .b( // getRegistry - IRegistry.aM // biome registry key - ); - } + private final Map>> h = new Object2ObjectArrayMap<>(); + public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) { - super(getStructureRegistry(), Optional.empty(), biomeProvider, biomeProvider, seed); + super(Registries.structureSet(), Optional.empty(), biomeProvider, biomeProvider, seed); this.delegate = pack.getGeneratorProvider().newInstance(pack); this.vanilla = vanilla; this.biomeSource = biomeProvider; @@ -141,6 +154,91 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { return y; } + @Override + public void a(IRegistryCustom iregistrycustom, StructureManager structuremanager, IChunkAccess ichunkaccess, + DefinedStructureManager definedstructuremanager, long i) { + super.a(iregistrycustom, structuremanager, ichunkaccess, definedstructuremanager, i); + } + + @Nullable + @Override + public List a(ConcentricRingsStructurePlacement concentricringsstructureplacement) { + this.i(); + return this.h.get(concentricringsstructureplacement).value(); + } + + private volatile boolean rings = false; + + @Override + public synchronized void i() { + if(!this.rings) { + super.i(); + this.populateStrongholdData(); + this.rings = true; + } + } + + private void populateStrongholdData() { + LOGGER.info("Generating safe stronghold data. This may take up to a minute."); + Set> set = this.d.b(); + a().map(h -> h.a()).forEach((holder) -> { // we dont need the spigot crap because it doesnt touch concentric. + StructurePlacement structureplacement = holder.b(); + if(structureplacement instanceof ConcentricRingsStructurePlacement concentricringsstructureplacement) { + if(holder.a().stream().anyMatch((structureset_a1) -> structureset_a1.a(set::contains))) { + this.h.put(concentricringsstructureplacement, + Lazy.lazy(() -> this.generateRingPositions(holder, concentricringsstructureplacement))); + } + } + }); + } + + private List generateRingPositions(StructureSet holder, + ConcentricRingsStructurePlacement concentricringsstructureplacement) { + if(concentricringsstructureplacement.d() == 0) { + return List.of(); + } + List list = new ArrayList<>(); + Set> set = holder.a().stream().flatMap((structureset_a) -> (structureset_a.a().a()).a().a()).collect( + Collectors.toSet()); + int i = concentricringsstructureplacement.b(); + int j = concentricringsstructureplacement.d(); + int k = concentricringsstructureplacement.c(); + Random random = new Random(); + random.setSeed(this.j); + double d0 = random.nextDouble() * Math.PI * 2.0; + int l = 0; + int i1 = 0; + + for(int j1 = 0; j1 < j; ++j1) { + double d1 = (double) (4 * i + i * i1 * 6) + (random.nextDouble() - 0.5) * (double) i * 2.5; + int k1 = (int) Math.round(Math.cos(d0) * d1); + int l1 = (int) Math.round(Math.sin(d0) * d1); + int i2 = SectionPosition.a(k1, 8); + int j2 = SectionPosition.a(l1, 8); + Objects.requireNonNull(set); + Pair> pair = this.c.a(i2, 0, j2, 112, set::contains, random, this.d()); + if(pair != null) { + BlockPosition blockposition = pair.getFirst(); + k1 = SectionPosition.a(blockposition.u()); + l1 = SectionPosition.a(blockposition.w()); + } + + list.add(new ChunkCoordIntPair(k1, l1)); + d0 += 6.283185307179586 / (double) k; + ++l; + if(l == k) { + ++i1; + l = 0; + k += 2 * k / (i1 + 1); + k = Math.min(k, j - j1); + d0 += random.nextDouble() * Math.PI * 2.0; + } + } + System.out.println(list); + return list; + + } + public int getMinimumY() { return h(); } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java index 3f5d27d15..48dffc3bd 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java @@ -1,5 +1,7 @@ package com.dfsek.terra.bukkit.nms; +import com.dfsek.terra.api.util.generic.Construct; + import net.minecraft.server.level.WorldServer; import net.minecraft.world.level.chunk.ChunkGenerator; import org.bukkit.World; @@ -11,8 +13,11 @@ import org.bukkit.event.world.WorldLoadEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; import com.dfsek.terra.api.config.ConfigPack; @@ -24,8 +29,6 @@ public class NMSInjectListener implements Listener { private static final Set INJECTED = new HashSet<>(); private static final ReentrantLock INJECT_LOCK = new ReentrantLock(); - private volatile boolean tags = false; - @EventHandler public void onWorldInit(WorldInitEvent event) { if (!INJECTED.contains(event.getWorld()) && event.getWorld().getGenerator() instanceof BukkitChunkGeneratorWrapper bukkitChunkGeneratorWrapper) { @@ -44,19 +47,12 @@ public class NMSInjectListener implements Listener { custom.conf = vanilla.conf; // world config from Spigot serverWorld.k().a.u = custom; - serverWorld.generator = null; - + LOGGER.info("Successfully injected into world."); + + serverWorld.k().a.u.i(); // generate stronghold data now + INJECT_LOCK.unlock(); } } - - @EventHandler - public void onWorldLoad(WorldLoadEvent load) { - if(!tags) { - System.out.println("Injecting tags late to prevent deadlock. Thank you Bukkit."); - tags = true; - NMSBiomeInjector.injectTags(); - } - } } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/Registries.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/Registries.java new file mode 100644 index 000000000..e9c110cc5 --- /dev/null +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/nms/Registries.java @@ -0,0 +1,31 @@ +package com.dfsek.terra.bukkit.nms; + +import net.minecraft.core.IRegistry; +import net.minecraft.core.IRegistryWritable; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.world.level.biome.BiomeBase; +import net.minecraft.world.level.levelgen.structure.StructureSet; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_18_R2.CraftServer; + + +public class Registries { + private static IRegistry getRegistry(ResourceKey> key) { + CraftServer craftserver = (CraftServer) Bukkit.getServer(); + DedicatedServer dedicatedserver = craftserver.getServer(); + return dedicatedserver + .aU() // getRegistryManager + .b( // getRegistry + key + ); + } + + public static IRegistry biomeRegistry() { + return getRegistry(IRegistry.aP); + } + + public static IRegistry structureSet() { + return getRegistry(IRegistry.aM); + } +}