From e6b083554bc92daf0da27907359af8ecd3f07e11 Mon Sep 17 00:00:00 2001 From: IronApollo Date: Tue, 13 Oct 2020 22:43:11 -0400 Subject: [PATCH] Implement biome-specific regen - initial biome-specific regen implementation - RegenOptions modified to include biomeType option (null if not specified) - Matched upstream's regen command --- .../worldedit/bukkit/adapter/Regenerator.java | 7 ++- .../adapter/impl/regen/Regen_v1_15_R2.java | 37 ++++++++++-- .../adapter/impl/regen/Regen_v1_16_R1.java | 48 +++++++++++++--- .../adapter/impl/regen/Regen_v1_16_R2.java | 57 +++++++++++++++++-- .../boydti/fawe/wrappers/WorldWrapper.java | 6 ++ .../worldedit/command/RegionCommands.java | 23 ++++++-- .../sk89q/worldedit/world/RegenOptions.java | 16 +++++- 7 files changed, 171 insertions(+), 23 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java index cbf099142..4bf5f1a7f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/Regenerator.java @@ -11,6 +11,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.RegenOptions; +import com.sk89q.worldedit.world.biome.BiomeType; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -266,9 +267,13 @@ public abstract class Regenerator factory = (AreaFactory) initAreaFactoryMethod.invoke(null, biomeconfig.b(), biomeconfig.c(), (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); - genLayerField.set(chunkManager, new FastGenLayer(factory)); + if (options.hasBiomeType()) { + BiomeBase biome = IRegistry.BIOME.get(MinecraftKey.a(options.getBiomeType().getId())); + chunkManager = new SingleBiomeWorldChunkManagerOverworld(biome); + } else { + chunkManager = new WorldChunkManagerOverworld(biomeconfig); + //replace genlayer + genLayerField.set(chunkManager, new FastGenLayer(factory)); + } return chunkManager; } + private static class SingleBiomeWorldChunkManagerOverworld extends WorldChunkManager { + + private final BiomeBase biome; + + public SingleBiomeWorldChunkManagerOverworld(BiomeBase biome) { + super(ImmutableSet.of(biome)); + this.biome = biome; + } + + @Override + public BiomeBase getBiome(int i, int i1, int i2) { + return biome; + } + } + private static class FastWorldGenContextArea implements AreaContextTransformed { private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java index af66ef955..7f6ea9630 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R1.java @@ -6,6 +6,7 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import com.sk89q.worldedit.bukkit.adapter.Regenerator; @@ -17,6 +18,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Path; +import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -53,15 +55,14 @@ import net.minecraft.server.v1_16_R1.GeneratorSettings; import net.minecraft.server.v1_16_R1.GeneratorSettingsFlat; import net.minecraft.server.v1_16_R1.IChunkAccess; import net.minecraft.server.v1_16_R1.IRegistry; -import net.minecraft.server.v1_16_R1.IRegistryCustom; import net.minecraft.server.v1_16_R1.LightEngineThreaded; import net.minecraft.server.v1_16_R1.LinearCongruentialGenerator; +import net.minecraft.server.v1_16_R1.MinecraftKey; import net.minecraft.server.v1_16_R1.MinecraftServer; import net.minecraft.server.v1_16_R1.NBTBase; import net.minecraft.server.v1_16_R1.NBTTagCompound; import net.minecraft.server.v1_16_R1.NoiseGeneratorPerlin; import net.minecraft.server.v1_16_R1.ProtoChunk; -import net.minecraft.server.v1_16_R1.RegistryReadOps; import net.minecraft.server.v1_16_R1.ResourceKey; import net.minecraft.server.v1_16_R1.World; import net.minecraft.server.v1_16_R1.WorldChunkManager; @@ -201,6 +202,14 @@ public class Regen_v1_16_R1 extends Regenerator factory = (AreaFactory) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); - genLayerField.set(chunkManager, new FastGenLayer(factory)); + AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largeBiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); + if (options.hasBiomeType()) { + BiomeBase biome = IRegistry.BIOME.get(MinecraftKey.a(options.getBiomeType().getId())); + chunkManager = new SingleBiomeWorldChunkManagerOverworld(biome); + } else { + chunkManager = new WorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largeBiomes); + //replace genLayer + genLayerField.set(chunkManager, new FastGenLayer(factory)); + } return chunkManager; } + private static class SingleBiomeWorldChunkManagerOverworld extends WorldChunkManager { + + private final BiomeBase biome; + + public SingleBiomeWorldChunkManagerOverworld(BiomeBase biome) { + super(Arrays.asList(biome)); + this.biome = biome; + } + + @Override + protected Codec a() { + return WorldChunkManagerOverworld.e; + } + + @Override + public BiomeBase getBiome(int i, int i1, int i2) { + return biome; + } + } + private static class FastWorldGenContextArea implements AreaContextTransformed { private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java index af58d56cf..52286b124 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/regen/Regen_v1_16_R2.java @@ -6,6 +6,7 @@ import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.bukkit.adapter.mc1_16_2.BukkitGetBlocks_1_16_2; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import com.sk89q.worldedit.bukkit.adapter.Regenerator; @@ -28,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BooleanSupplier; import java.util.function.LongFunction; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.annotation.Nullable; import net.minecraft.server.v1_16_R2.Area; import net.minecraft.server.v1_16_R2.AreaContextTransformed; @@ -56,11 +58,14 @@ import net.minecraft.server.v1_16_R2.IRegistry; import net.minecraft.server.v1_16_R2.IRegistryCustom; import net.minecraft.server.v1_16_R2.LightEngineThreaded; import net.minecraft.server.v1_16_R2.LinearCongruentialGenerator; +import net.minecraft.server.v1_16_R2.MinecraftKey; import net.minecraft.server.v1_16_R2.MinecraftServer; import net.minecraft.server.v1_16_R2.NBTBase; import net.minecraft.server.v1_16_R2.NBTTagCompound; import net.minecraft.server.v1_16_R2.NoiseGeneratorPerlin; import net.minecraft.server.v1_16_R2.ProtoChunk; +import net.minecraft.server.v1_16_R2.RegistryGeneration; +import net.minecraft.server.v1_16_R2.RegistryMaterials; import net.minecraft.server.v1_16_R2.RegistryReadOps; import net.minecraft.server.v1_16_R2.ResourceKey; import net.minecraft.server.v1_16_R2.World; @@ -202,6 +207,14 @@ public class Regen_v1_16_R2 extends Regenerator biomeRegistry = (IRegistry) biomeRegistryField.get(chunkManager); - chunkManager = new WorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes, biomeRegistry); + IRegistry biomeRegistrynms = (IRegistry) biomeRegistryField.get(chunkManager); + IRegistry biomeRegistry; + if (options.hasBiomeType()) { + BiomeBase biome = RegistryGeneration.WORLDGEN_BIOME.get(MinecraftKey.a(options.getBiomeType().getId())); + biomeRegistry = new RegistryMaterials<>(ResourceKey.a(new MinecraftKey("fawe_biomes")), Lifecycle.experimental()); + ((RegistryMaterials) biomeRegistry).a(0, RegistryGeneration.WORLDGEN_BIOME.c(biome).get(), biome, Lifecycle.experimental()); + } else { + biomeRegistry = biomeRegistrynms; + } + chunkManager = new FastWorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes, biomeRegistry); //replace genLayer AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l))); - genLayerField.set(chunkManager, new FastGenLayer(factory)); + ((FastWorldChunkManagerOverworld) chunkManager).genLayer = new FastGenLayer(factory); return chunkManager; } + private static class FastWorldChunkManagerOverworld extends WorldChunkManager { + + private GenLayer genLayer; + private final IRegistry k; + private final boolean isSingleRegistry; + + public FastWorldChunkManagerOverworld(long seed, boolean legacyBiomeInitLayer, boolean largeBiomes, IRegistry biomeRegistry) { + super(biomeRegistry.g().collect(Collectors.toList())); + this.k = biomeRegistry; + this.isSingleRegistry = biomeRegistry.d().size() == 1; + this.genLayer = GenLayers.a(seed, legacyBiomeInitLayer, largeBiomes ? 6 : 4, 4); + } + + @Override + protected Codec a() { + return WorldChunkManagerOverworld.e; + } + + @Override + public BiomeBase getBiome(int i, int i1, int i2) { + if (this.isSingleRegistry) { + return this.k.fromId(0); + } + return this.genLayer.a(this.k, i, i2); + } + } + + private static class FastWorldGenContextArea implements AreaContextTransformed { private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java index 1a8100ec0..ff2ab4a3b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java @@ -29,6 +29,7 @@ import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -242,6 +243,11 @@ public class WorldWrapper extends AbstractWorld { return parent.regenerate(region, session); } + @Override + public boolean regenerate(Region region, Extent extent, RegenOptions options) { + return parent.regenerate(region, extent, options); + } + @Override public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException { return TaskManager.IMP.sync(() -> { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index d357d111d..be2a307b9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -63,7 +63,9 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import org.enginehub.piston.annotation.Command; @@ -609,15 +611,26 @@ public class RegionCommands { @CommandPermissions("worldedit.regen") @Logging(REGION) @Confirm(Confirm.Processor.REGION) - public void regenerateChunk(Actor actor, World world, LocalSession session, EditSession editSession, - @Selection Region region) throws WorldEditException { + void regenerate(Actor actor, World world, LocalSession session, EditSession editSession, + @Selection Region region, + @Arg(desc = "The seed to regenerate with, otherwise uses world seed", def = "") + Long seed, + @Switch(name = 'b', desc = "Regenerate biomes as well") + boolean regenBiomes, + @Arg(desc = "Biome to apply for this regeneration (only works in overworld)", def = "") + BiomeType biomeType) throws WorldEditException { Mask mask = session.getMask(); boolean success; try { - session.setMask((Mask) null); - session.setSourceMask((Mask) null); + session.setMask(null); + session.setSourceMask(null); actor.printInfo(TranslatableComponent.of("fawe.regen.time")); - success = world.regenerate(region, editSession); + RegenOptions options = RegenOptions.builder() + .seed(seed) + .regenBiomes(regenBiomes) + .biomeType(biomeType) + .build(); + success = world.regenerate(region, editSession, options); } finally { session.setMask(mask); session.setSourceMask(mask); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/RegenOptions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/RegenOptions.java index 3fcb79307..6cf2b80b2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/RegenOptions.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/RegenOptions.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.world; import com.google.auto.value.AutoValue; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.biome.BiomeType; import java.util.OptionalLong; import javax.annotation.Nullable; @@ -38,7 +39,7 @@ public abstract class RegenOptions { * @return the builder */ public static Builder builder() { - return new AutoValue_RegenOptions.Builder().seed(OptionalLong.empty()).regenBiomes(false); + return new AutoValue_RegenOptions.Builder().seed(OptionalLong.empty()).regenBiomes(false).biomeType(null); } @AutoValue.Builder @@ -69,6 +70,13 @@ public abstract class RegenOptions { */ public abstract Builder regenBiomes(boolean regenBiomes); + /** + * Defines the {@code BiomeType} the regenerator should use for regeneration. Defaults to {@code null}. + * @param biomeType the {@code BiomeType} to be used for regeneration + * @return this builder + */ + public abstract Builder biomeType(@Nullable BiomeType biomeType); + /** * Build the options object. * @@ -99,4 +107,10 @@ public abstract class RegenOptions { return isRegenBiomes(); } + @Nullable public abstract BiomeType getBiomeType(); + + public boolean hasBiomeType() { + return getBiomeType() != null; + } + }