Implement generation of biome decorations to //regen <biome> and add option for a random seed to be used (#1819)

This commit is contained in:
Jordan 2022-06-19 22:33:16 +01:00 committed by GitHub
parent 692a010c39
commit dac3610bcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 138 additions and 30 deletions

View File

@ -11,7 +11,6 @@ import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Lifecycle;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightGetBlocks;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
@ -38,6 +37,7 @@ import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.FixedBiomeSource;
import net.minecraft.world.level.biome.OverworldBiomeSource;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
@ -67,6 +67,7 @@ import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_17_R1.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
@ -213,6 +214,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
BiomeProvider biomeProvider = getBiomeProvider();
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
@ -246,7 +249,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
false,
environment,
generator,
originalBukkitWorld.getBiomeProvider()
biomeProvider
) {
private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(
options
@ -280,9 +283,16 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator) {
Supplier<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Supplier<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
.get(originalChunkProvider.getGenerator());
BiomeSource biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
if (biomeSource instanceof OverworldBiomeSource) {
biomeSource = fastOverworldBiomeSource(biomeSource);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options
.getBiomeType()
.getId())));
} else {
biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
if (biomeSource instanceof OverworldBiomeSource) {
biomeSource = fastOverworldBiomeSource(biomeSource);
}
}
chunkGenerator = new NoiseBasedChunkGenerator(biomeSource, seed, generatorSettingBaseSupplier);
} else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator) {

View File

@ -17,6 +17,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
@ -33,6 +34,7 @@ import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.FixedBiomeSource;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkStatus;
@ -46,6 +48,7 @@ import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
@ -54,6 +57,7 @@ import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
@ -81,6 +85,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
private static final Field generatorSettingBaseSupplierField;
private static final Field delegateField;
private static final Field chunkSourceField;
private static final Field ringPositionsField;
private static final Field hasGeneratedPositionsField;
//list of chunk stati in correct order without FULL
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
@ -139,6 +145,12 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K"));
chunkSourceField.setAccessible(true);
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "i"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "j"));
hasGeneratedPositionsField.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -198,6 +210,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
BiomeProvider biomeProvider = getBiomeProvider();
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData();
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
@ -232,7 +246,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
false,
environment,
generator,
originalBukkitWorld.getBiomeProvider()
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.asHolderIdMap().byId(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
@ -260,22 +274,30 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
}
//generator
if (originalChunkProvider.getGenerator() instanceof FlatLevelSource flatLevelSource) {
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
chunkGenerator = new FlatLevelSource(originalChunkProvider.getGenerator().structureSets, generatorSettingFlat);
} else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
chunkGenerator = new FlatLevelSource(originalGenerator.structureSets, generatorSettingFlat);
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier =
(Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
.get(originalChunkProvider.getGenerator());
BiomeSource biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
chunkGenerator = new NoiseBasedChunkGenerator(originalChunkProvider.getGenerator().structureSets, noiseBasedChunkGenerator.noises,
.get(originalGenerator);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME
.asHolderIdMap()
.byId(WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())));
} else {
biomeSource = originalGenerator.getBiomeSource();
}
chunkGenerator = new NoiseBasedChunkGenerator(originalGenerator.structureSets, noiseBasedChunkGenerator.noises,
biomeSource, seed,
generatorSettingBaseSupplier
);
} else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator customChunkGenerator) {
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
chunkGenerator = customChunkGenerator.delegate;
} else {
LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName());
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
return false;
}
if (generator != null) {
@ -283,6 +305,20 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
generateConcurrent = generator.isParallelCapable();
}
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
// Optimisation for needless ring position calculation when the seed and biome is the same.
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(originalGenerator);
if (hasGeneratedPositions) {
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions =
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(
originalGenerator);
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(ringPositions);
ringPositionsField.set(chunkGenerator, copy);
hasGeneratedPositionsField.setBoolean(chunkGenerator, true);
}
}
chunkGenerator.conf = originalServerWorld.spigotConfig;
freshChunkProvider = new ServerChunkCache(
freshWorld,

View File

@ -17,6 +17,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.io.file.SafeFiles;
import com.sk89q.worldedit.world.RegenOptions;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
@ -36,6 +37,7 @@ import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.FixedBiomeSource;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkStatus;
@ -49,6 +51,7 @@ import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
@ -57,6 +60,7 @@ import org.bukkit.Bukkit;
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.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
@ -84,6 +88,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
private static final Field generatorSettingBaseSupplierField;
private static final Field delegateField;
private static final Field chunkSourceField;
private static final Field ringPositionsField;
private static final Field hasGeneratedPositionsField;
//list of chunk stati in correct order without FULL
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
@ -142,6 +148,12 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "L"));
chunkSourceField.setAccessible(true);
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "i"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "j"));
hasGeneratedPositionsField.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -218,6 +230,8 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
);
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
BiomeProvider biomeProvider = getBiomeProvider();
//init world
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
server,
@ -233,7 +247,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
false,
environment,
generator,
originalBukkitWorld.getBiomeProvider()
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.asHolderIdMap().byId(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
@ -260,27 +274,30 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
}
//generator
// TODO figure out if this is needed. We can probably use
// chunkGenerator = newOpts.dimensions().getOrThrow(levelStemResourceKey).generator();
// instead.
if (originalChunkProvider.getGenerator() instanceof FlatLevelSource flatLevelSource) {
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
chunkGenerator = new FlatLevelSource(originalChunkProvider.getGenerator().structureSets, generatorSettingFlat);
} else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier =
(Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
.get(originalChunkProvider.getGenerator());
BiomeSource biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
chunkGenerator = new NoiseBasedChunkGenerator(originalChunkProvider.getGenerator().structureSets,
chunkGenerator = new FlatLevelSource(originalGenerator.structureSets, generatorSettingFlat);
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
originalGenerator);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME
.asHolderIdMap()
.byId(WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())));
} else {
biomeSource = originalGenerator.getBiomeSource();
}
chunkGenerator = new NoiseBasedChunkGenerator(originalGenerator.structureSets,
noiseBasedChunkGenerator.noises,
biomeSource,
generatorSettingBaseSupplier
);
} else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator customChunkGenerator) {
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
chunkGenerator = customChunkGenerator.delegate;
} else {
LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName());
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
return false;
}
if (generator != null) {
@ -289,6 +306,19 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
}
chunkGenerator.conf = freshWorld.spigotConfig;
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
// Optimisation for needless ring position calculation when the seed and biome is the same.
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(originalGenerator);
if (hasGeneratedPositions) {
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions =
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(
originalGenerator);
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(ringPositions);
ringPositionsField.set(chunkGenerator, copy);
hasGeneratedPositionsField.setBoolean(chunkGenerator, true);
}
}
freshChunkProvider = new ServerChunkCache(
freshWorld,
session,
@ -529,6 +559,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
return CompletableFuture.completedFuture(chunk);
}
}
}

View File

@ -7,6 +7,7 @@ import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.pattern.Pattern;
@ -22,10 +23,14 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.apache.logging.log4j.Logger;
import org.bukkit.World;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.WorldInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -521,6 +526,13 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
return tasks;
}
protected BiomeProvider getBiomeProvider() {
if (options.hasBiomeType()) {
return new SingleBiomeProvider();
}
return originalBukkitWorld.getBiomeProvider();
}
//classes
public enum Concurrency {
@ -624,4 +636,20 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
}
public class SingleBiomeProvider extends BiomeProvider {
private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType());
@Override
public org.bukkit.block.Biome getBiome(final WorldInfo worldInfo, final int x, final int y, final int z) {
return biome;
}
@Override
public List<org.bukkit.block.Biome> getBiomes(final WorldInfo worldInfo) {
return Collections.singletonList(biome);
}
}
}

View File

@ -79,6 +79,7 @@ import org.enginehub.piston.annotation.param.Switch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL;
import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION;
@ -691,6 +692,8 @@ public class RegionCommands {
Long seed,
@Switch(name = 'b', desc = "Regenerate biomes as well")
boolean regenBiomes,
@Switch(name = 'r', desc = "If the seed should be randomized")
boolean randomSeed,
@Arg(desc = "Biome to apply for this regeneration (only works in overworld)", def = "")
BiomeType biomeType
) throws WorldEditException {
@ -703,7 +706,7 @@ public class RegionCommands {
actor.print(Caption.of("fawe.regen.time"));
//FAWE end
RegenOptions options = RegenOptions.builder()
.seed(seed)
.seed(!randomSeed ? seed : new Long(ThreadLocalRandom.current().nextLong()))
.regenBiomes(regenBiomes)
.biomeType(biomeType)
.build();