diff --git a/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_18_R1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_18_R1/PaperweightAdapter.java index 01b5ac23f..c30dfe662 100644 --- a/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_18_R1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_18_R1/PaperweightAdapter.java @@ -673,7 +673,9 @@ public final class PaperweightAdapter implements BukkitImplAdapter public static + public static WorldGenSettings replaceSeed(ServerLevel originalWorld, long seed, WorldGenSettings originalOpts) { + // FAWE end RegistryWriteOps nbtReadRegOps = RegistryWriteOps.create( NbtOps.INSTANCE, originalWorld.getServer().registryAccess() @@ -700,8 +702,10 @@ public final class PaperweightAdapter implements BukkitImplAdapter private static @SuppressWarnings("unchecked") - private Dynamic recursivelySetSeed( + private static Dynamic recursivelySetSeed( + // FAWE end Dynamic dynamic, long seed, Set> seen diff --git a/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/PaperweightFaweAdapter.java index c04b4254a..ad53a1789 100644 --- a/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/PaperweightFaweAdapter.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R1.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1.nbt.PaperweightLazyCompoundTag; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1.regen.PaperweightRegen; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -616,8 +617,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { -// return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); - return false; + return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/regen/PaperweightRegen.java index 85fb06d67..c2ce83d38 100644 --- a/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R1/regen/PaperweightRegen.java @@ -1,722 +1,501 @@ -//package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1.regen; -// -//import com.fastasyncworldedit.bukkit.adapter.Regenerator; -//import com.fastasyncworldedit.core.Fawe; -//import com.fastasyncworldedit.core.queue.IChunkCache; -//import com.fastasyncworldedit.core.queue.IChunkGet; -//import com.fastasyncworldedit.core.util.ReflectionUtils; -//import com.fastasyncworldedit.core.util.TaskManager; -//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.Refraction; -//import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1.PaperweightGetBlocks; -//import com.sk89q.worldedit.extent.Extent; -//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 io.papermc.lib.PaperLib; -//import net.minecraft.core.MappedRegistry; -//import net.minecraft.core.Registry; -//import net.minecraft.core.RegistryAccess; -//import net.minecraft.data.BuiltinRegistries; -//import net.minecraft.data.worldgen.biome.Biomes; -//import net.minecraft.nbt.CompoundTag; -//import net.minecraft.nbt.NbtOps; -//import net.minecraft.resources.RegistryReadOps; -//import net.minecraft.resources.ResourceKey; -//import net.minecraft.resources.ResourceLocation; -//import net.minecraft.server.MinecraftServer; -//import net.minecraft.server.level.ServerChunkCache; -//import net.minecraft.server.level.ServerLevel; -//import net.minecraft.server.level.ThreadedLevelLightEngine; -//import net.minecraft.server.level.progress.ChunkProgressListener; -//import net.minecraft.util.LinearCongruentialGenerator; -//import net.minecraft.world.level.ChunkPos; -//import net.minecraft.world.level.Level; -//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.MultiNoiseBiomeSource; -//import net.minecraft.world.level.chunk.ChunkAccess; -//import net.minecraft.world.level.chunk.ChunkGenerator; -//import net.minecraft.world.level.chunk.ChunkStatus; -//import net.minecraft.world.level.chunk.LevelChunk; -//import net.minecraft.world.level.chunk.ProtoChunk; -//import net.minecraft.world.level.chunk.UpgradeData; -//import net.minecraft.world.level.dimension.LevelStem; -//import net.minecraft.world.level.levelgen.FlatLevelSource; -//import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; -//import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -//import net.minecraft.world.level.levelgen.WorldGenSettings; -//import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -//import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; -//import net.minecraft.world.level.levelgen.synth.ImprovedNoise; -//import net.minecraft.world.level.storage.LevelStorageSource; -//import net.minecraft.world.level.storage.PrimaryLevelData; -//import org.apache.logging.log4j.Logger; -//import org.bukkit.Bukkit; -//import org.bukkit.craftbukkit.v1_18_R1.CraftServer; -//import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; -//import org.bukkit.craftbukkit.v1_18_R1.generator.CustomChunkGenerator; -//import org.bukkit.generator.BlockPopulator; -// -//import javax.annotation.Nullable; -//import java.io.IOException; -//import java.lang.reflect.Field; -//import java.lang.reflect.Method; -//import java.nio.file.Path; -//import java.util.Collections; -//import java.util.HashSet; -//import java.util.LinkedHashMap; -//import java.util.List; -//import java.util.Map; -//import java.util.Random; -//import java.util.Set; -//import java.util.concurrent.CompletableFuture; -//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; -// -//public class PaperweightRegen extends Regenerator { -// -// private static final Logger LOGGER = LogManagerCompat.getLogger(); -// -// private static final Field serverWorldsField; -// private static final Field worldPaperConfigField; -// private static final Field flatBedrockField; -// private static final Field generatorSettingFlatField; -// private static final Field generatorSettingBaseSupplierField; -// private static final Field delegateField; -// private static final Field chunkProviderField; -// -// //list of chunk stati in correct order without FULL -// private static final Map chunkStati = new LinkedHashMap<>(); -// -// static { -// chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing -// chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps -// chunkStati.put( -// ChunkStatus.STRUCTURE_REFERENCES, -// Concurrency.FULL -// ); // structure refs: radius 8, but only writes to current chunk -// chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0 -// chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8 -// chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE -// chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results -// chunkStati.put( -// ChunkStatus.LIQUID_CARVERS, -// Concurrency.NONE -// ); // liquid carvers: radius 0, but RADIUS and FULL change results -// chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps -// chunkStati.put( -// ChunkStatus.LIGHT, -// Concurrency.FULL -// ); // light: radius 1, but no writes to other chunks, only current chunk -// chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0 -// chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0 -// -// try { -// serverWorldsField = CraftServer.class.getDeclaredField("worlds"); -// serverWorldsField.setAccessible(true); -// -// Field tmpPaperConfigField; -// Field tmpFlatBedrockField; -// try { //only present on paper -// tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); -// tmpPaperConfigField.setAccessible(true); -// -// tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); -// tmpFlatBedrockField.setAccessible(true); -// } catch (Exception e) { -// tmpPaperConfigField = null; -// tmpFlatBedrockField = null; -// } -// worldPaperConfigField = tmpPaperConfigField; -// flatBedrockField = tmpFlatBedrockField; -// -// generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( -// "settings", "g")); -// generatorSettingBaseSupplierField.setAccessible(true); -// -// generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "e")); -// generatorSettingFlatField.setAccessible(true); -// -// delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); -// delegateField.setAccessible(true); -// -// chunkProviderField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "C")); -// chunkProviderField.setAccessible(true); -// } catch (Exception e) { -// throw new RuntimeException(e); -// } -// } -// -// //runtime -// private ServerLevel originalServerWorld; -// private ServerChunkCache originalChunkProvider; -// private ServerLevel freshWorld; -// private ServerChunkCache freshChunkProvider; -// private LevelStorageSource.LevelStorageAccess session; -// private StructureManager structureManager; -// private ThreadedLevelLightEngine threadedLevelLightEngine; -// private ChunkGenerator chunkGenerator; -// -// private Path tempDir; -// -// private boolean generateFlatBedrock = false; -// -// public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { -// super(originalBukkitWorld, region, target, options); -// } -// -// @Override -// protected boolean prepare() { -// this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); -// originalChunkProvider = originalServerWorld.getChunkSource(); -// if (!(originalChunkProvider instanceof ServerChunkCache)) { -// return false; -// } -// -// //flat bedrock? (only on paper) -// if (worldPaperConfigField != null) { -// try { -// generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalServerWorld)); -// } catch (Exception ignored) { -// } -// } -// -// seed = options.getSeed().orElse(originalServerWorld.getSeed()); -// chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c)); -// -// return true; -// } -// -// @Override -// protected boolean initNewWorld() throws Exception { -// //world folder -// tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); -// -// //prepare for world init (see upstream implementation for reference) -// org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); -// org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); -// LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); -// ResourceKey levelStemResourceKey = getWorldDimKey(environment); -// session = levelStorageSource.createAccess("worldeditregentempworld", levelStemResourceKey); -// PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData; -// -// MinecraftServer server = originalServerWorld.getCraftServer().getServer(); -// PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData(); -// RegistryReadOps nbtRegOps = RegistryReadOps.createAndLoad( -// NbtOps.INSTANCE, server.resources.getResourceManager(), -// RegistryAccess.builtin() -// ); -// WorldGenSettings newOpts = WorldGenSettings.CODEC -// .encodeStart(nbtRegOps, levelProperties.worldGenSettings()) -// .flatMap(tag -> WorldGenSettings.CODEC.parse(this.recursivelySetSeed( -// new Dynamic<>(nbtRegOps, tag), -// seed, -// new HashSet<>() -// ))) -// .result() -// .orElseThrow(() -> new IllegalStateException("Unable to map GeneratorOptions")); -// LevelSettings newWorldSettings = new LevelSettings( -// "worldeditregentempworld", -// originalWorldData.settings.gameType(), -// originalWorldData.settings.hardcore(), -// originalWorldData.settings.difficulty(), -// originalWorldData.settings.allowCommands(), -// originalWorldData.settings.gameRules(), -// originalWorldData.settings.getDataPackConfig() -// ); -// PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable()); -// -// //init world -// freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( -// server, -// server.executor, -// session, -// newWorldData, -// originalServerWorld.dimension(), -// originalServerWorld.dimensionType(), -// new RegenNoOpWorldLoadListener(), -// // placeholder. Required for new ChunkProviderServer, but we create and then set it later -// newOpts.dimensions().get(levelStemResourceKey).generator(), -// originalServerWorld.isDebug(), -// seed, -// ImmutableList.of(), -// false, -// environment, -// generator, -// originalBukkitWorld.getBiomeProvider() -// ) { -// private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse( -// options -// .getBiomeType() -// .getId())) : null; -// -// @Override -// public void tick(BooleanSupplier shouldKeepTicking) { //no ticking -// } -// -// @Override -// public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { -// if (options.hasBiomeType()) { -// return singleBiome; -// } -// return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ, -// PaperweightRegen.this.chunkGenerator.climateSampler()); -// } -// }).get(); -// freshWorld.noSave = true; -// removeWorldFromWorldsMap(); -// newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name -// if (worldPaperConfigField != null) { -// worldPaperConfigField.set(freshWorld, originalServerWorld.paperConfig); -// } -// -// //generator -// if (originalChunkProvider.getGenerator() instanceof FlatLevelSource) { -// FlatLevelGeneratorSettings generatorSettingFlat = (FlatLevelGeneratorSettings) generatorSettingFlatField.get( -// originalChunkProvider.getGenerator()); -// chunkGenerator = new FlatLevelSource(generatorSettingFlat); -// } else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator) { -// Supplier generatorSettingBaseSupplier = (Supplier) generatorSettingBaseSupplierField -// .get(originalChunkProvider.getGenerator()); -// BiomeSource biomeSource = originalChunkProvider.getGenerator().getBiomeSource(); -// if (biomeSource instanceof MultiNoiseBiomeSource) { -// biomeSource = fastMultiNoiseBiomeSource(biomeSource); -// } -// chunkGenerator = new NoiseBasedChunkGenerator(((NoiseBasedChunkGenerator) chunkGenerator).noises, biomeSource, seed, -// generatorSettingBaseSupplier -// ); -// } else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator) { -// chunkGenerator = (ChunkGenerator) delegateField.get(originalChunkProvider.getGenerator()); -// } else { -// LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName()); -// return false; -// } -// if (generator != null) { -// chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); -// generateConcurrent = generator.isParallelCapable(); -// } -// -// freshChunkProvider = new ServerChunkCache( -// freshWorld, -// session, -// server.getFixerUpper(), -// server.getStructureManager(), -// server.executor, -// chunkGenerator, -// freshWorld.spigotConfig.viewDistance, -// server.forceSynchronousWrites(), -// new RegenNoOpWorldLoadListener(), -// (chunkCoordIntPair, state) -> { -// }, -// () -> server.overworld().getDataStorage() -// ) { -// // redirect to LevelChunks created in #createChunks -// @Override -// public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean flag) { -// return getChunkAt(x, z); -// } -// }; -// -// ReflectionUtils.unsafeSet(chunkProviderField, freshWorld, freshChunkProvider); -// //let's start then -// structureManager = server.getStructureManager(); -// threadedLevelLightEngine = freshChunkProvider.getLightEngine(); -// -// return true; -// } -// -// @Override -// protected void cleanup() { -// try { -// session.close(); -// } catch (Exception ignored) { -// } -// -// //shutdown chunk provider -// try { -// Fawe.instance().getQueueHandler().sync(() -> { -// try { -// freshChunkProvider.close(false); -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// }); -// } catch (Exception ignored) { -// } -// -// //remove world from server -// try { -// Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); -// } catch (Exception ignored) { -// } -// -// //delete directory -// try { -// SafeFiles.tryHardToDeleteDir(tempDir); -// } catch (Exception ignored) { -// } -// } -// -// @Override -// protected ProtoChunk createProtoChunk(int x, int z) { -// return PaperLib.isPaper() -// ? new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, freshWorld) // paper -// : new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld); // spigot -// } -// -// @Override -// protected LevelChunk createChunk(ProtoChunk protoChunk) { -// return new LevelChunk( -// freshWorld, -// protoChunk, -// null // we don't want to add entities -// ); -// } -// -// @Override -// protected ChunkStatusWrap getFullChunkStatus() { -// return new ChunkStatusWrap(ChunkStatus.FULL); -// } -// -// @Override -// protected List getBlockPopulators() { -// return originalServerWorld.getWorld().getPopulators(); -// } -// -// @Override -// protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { -// // BlockPopulator#populate has to be called synchronously for TileEntity access -// TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk())); -// } -// -// @Override -// protected IChunkCache initSourceQueueCache() { -// return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { -// @Override -// public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { -// return getChunkAt(x, z); -// } -// }; -// } -// -// //util -// private void removeWorldFromWorldsMap() { -// Fawe.instance().getQueueHandler().sync(() -> { -// try { -// Map map = (Map) serverWorldsField.get(Bukkit.getServer()); -// map.remove("worldeditregentempworld"); -// } catch (IllegalAccessException e) { -// throw new RuntimeException(e); -// } -// }); -// } -// -// private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { -// switch (env) { -// case NETHER: -// return LevelStem.NETHER; -// case THE_END: -// return LevelStem.END; -// case NORMAL: -// default: -// return LevelStem.OVERWORLD; -// } -// } -// -// private Dynamic recursivelySetSeed( -// Dynamic dynamic, -// long seed, -// Set> dynamicSet -// ) { -// return !dynamicSet.add(dynamic) ? dynamic : dynamic.updateMapValues((pair) -> { -// if (pair.getFirst().asString("").equals("seed")) { -// return pair.mapSecond((v) -> v.createLong(seed)); -// } else { -// return ((Dynamic) pair.getSecond()).getValue() instanceof CompoundTag -// ? pair.mapSecond((v) -> this.recursivelySetSeed((Dynamic) v, seed, dynamicSet)) -// : pair; -// -// } -// }); -// } -// -// private BiomeSource fastMultiNoiseBiomeSource(BiomeSource biomeSource) throws Exception { -// Field legacyBiomeInitLayerField = MultiNoiseBiomeSource.class.getDeclaredField( -// Refraction.pickName("legacyBiomeInitLayer", "i")); -// legacyBiomeInitLayerField.setAccessible(true); -// Field largeBiomesField = NoiseGeneratorSettings.class.getDeclaredField(Refraction.pickName("largeBiomes", "j")); -// largeBiomesField.setAccessible(true); -// Field biomeRegistryField = MultiNoiseBiomeSource.class.getDeclaredField(Refraction.pickName("biomes", "k")); -// biomeRegistryField.setAccessible(true); -// Field areaLazyField = Layer.class.getDeclaredField(Refraction.pickName("area", "b")); -// areaLazyField.setAccessible(true); -// Method initAreaFactoryMethod = Layers.class.getDeclaredMethod( -// Refraction.pickName("getDefaultLayer", "a"), -// boolean.class, -// int.class, -// int.class, -// LongFunction.class -// ); -// initAreaFactoryMethod.setAccessible(true); -// -// //init new WorldChunkManagerOverworld -// boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(biomeSource); -// boolean largebiomes = largeBiomesField.getBoolean(biomeSource); -// Registry biomeRegistryMojang = (Registry) biomeRegistryField.get(biomeSource); -// Registry biomeRegistry; -// if (options.hasBiomeType()) { -// Biome biome = BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options.getBiomeType().getId())); -// biomeRegistry = new MappedRegistry<>( -// ResourceKey.createRegistryKey(new ResourceLocation("fawe_biomes")), -// Lifecycle.experimental() -// ); -// ((MappedRegistry) biomeRegistry).registerMapping(0, BuiltinRegistries.BIOME.getResourceKey(biome).get(), biome, -// Lifecycle.experimental() -// ); -// } else { -// biomeRegistry = biomeRegistryMojang; -// } -// -// //replace genLayer -// AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke( -// null, -// legacyBiomeInitLayer, -// largebiomes ? 6 : 4, -// 4, -// (LongFunction) (salt -> new FastWorldGenContextArea(seed, salt)) -// ); -// biomeSource = new FastOverworldBiomeSource(biomeRegistry, new FastGenLayer(factory)); -// -// return biomeSource; -// } -// -// private static class FastOverworldBiomeSource extends BiomeSource { -// -// private final Registry biomeRegistry; -// private final boolean isSingleRegistry; -// private final FastGenLayer fastGenLayer; -// -// public FastOverworldBiomeSource( -// Registry biomeRegistry, -// FastGenLayer genLayer -// ) { -// super(biomeRegistry.stream().collect(Collectors.toList())); -// this.biomeRegistry = biomeRegistry; -// this.isSingleRegistry = biomeRegistry.entrySet().size() == 1; -// this.fastGenLayer = genLayer; -// } -// -// @Override -// protected Codec codec() { -// return MultiNoiseBiomeSource.CODEC; -// } -// -// @Override -// public BiomeSource withSeed(final long seed) { -// return null; -// } -// -// @Override -// public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) { -// if (this.isSingleRegistry) { -// return this.biomeRegistry.byId(0); -// } -// return this.fastGenLayer.get(this.biomeRegistry, biomeX, biomeZ); -// } -// -// } -// -// private static class FastWorldGenContextArea implements BigContext { -// -// private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); -// private final ImprovedNoise improvedNoise; -// private final long magicrandom; -// private final ConcurrentHashMap map = new ConcurrentHashMap<>(); //needed for multithreaded generation -// -// public FastWorldGenContextArea(long seed, long lconst) { -// this.magicrandom = mix(seed, lconst); -// this.improvedNoise = new ImprovedNoise(new SimpleRandomSource(seed)); -// } -// -// private static long mix(long seed, long salt) { -// long l = LinearCongruentialGenerator.next(salt, salt); -// l = LinearCongruentialGenerator.next(l, salt); -// l = LinearCongruentialGenerator.next(l, salt); -// long m = LinearCongruentialGenerator.next(seed, l); -// m = LinearCongruentialGenerator.next(m, l); -// m = LinearCongruentialGenerator.next(m, l); -// return m; -// } -// -// @Override -// public FastAreaLazy createResult(PixelTransformer pixelTransformer) { -// return new FastAreaLazy(sharedAreaMap, pixelTransformer); -// } -// -// @Override -// public void initRandom(long x, long z) { -// long l = this.magicrandom; -// l = LinearCongruentialGenerator.next(l, x); -// l = LinearCongruentialGenerator.next(l, z); -// l = LinearCongruentialGenerator.next(l, x); -// l = LinearCongruentialGenerator.next(l, z); -// this.map.put(Thread.currentThread().getId(), l); -// } -// -// @Override -// public int nextRandom(int y) { -// long tid = Thread.currentThread().getId(); -// long e = this.map.computeIfAbsent(tid, i -> 0L); -// int mod = (int) Math.floorMod(e >> 24L, (long) y); -// this.map.put(tid, LinearCongruentialGenerator.next(e, this.magicrandom)); -// return mod; -// } -// -// @Override -// public ImprovedNoise getBiomeNoise() { -// return this.improvedNoise; -// } -// -// } -// -// private static class FastGenLayer extends Layer { -// -// private final FastAreaLazy fastAreaLazy; -// -// public FastGenLayer(AreaFactory factory) { -// super(() -> null); -// this.fastAreaLazy = factory.make(); -// } -// -// @Override -// public Biome get(Registry registry, int x, int z) { -// ResourceKey key = Biomes.byId(this.fastAreaLazy.get(x, z)); -// if (key == null) { -// return registry.get(Biomes.byId(0)); -// } -// Biome biome = registry.get(key); -// if (biome == null) { -// return registry.get(Biomes.byId(0)); -// } -// return biome; -// } -// -// } -// -// private static class FastAreaLazy implements Area { -// -// private final PixelTransformer transformer; -// //ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a synchronized context -// //using a map for each thread worsens the performance significantly due to cache misses (factor 5) -// private final ConcurrentHashMap sharedMap; -// -// public FastAreaLazy(ConcurrentHashMap sharedMap, PixelTransformer transformer) { -// this.sharedMap = sharedMap; -// this.transformer = transformer; -// } -// -// @Override -// public int get(int x, int z) { -// long zx = ChunkPos.asLong(x, z); -// return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z)); -// } -// -// } -// -// private static class RegenNoOpWorldLoadListener implements ChunkProgressListener { -// -// private RegenNoOpWorldLoadListener() { -// } -// -// @Override -// public void updateSpawnPos(ChunkPos spawnPos) { -// } -// -// @Override -// public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { -// } -// -// @Override -// public void start() { -// -// } -// -// @Override -// public void stop() { -// } -// -// // TODO Paper only(?) @Override -// public void setChunkRadius(int radius) { -// } -// -// } -// -// private class FastProtoChunk extends ProtoChunk { -// -// // avoid warning on paper -// public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world, ServerLevel serverLevel) { -// super(pos, upgradeData, world, serverLevel); -// } -// -// // compatibility with spigot -// public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor) { -// super(pos, upgradeData, levelHeightAccessor); -// } -// -// public boolean generateFlatBedrock() { -// return generateFlatBedrock; -// } -// -// // no one will ever see the entities! -// @Override -// public List getEntities() { -// return Collections.emptyList(); -// } -// -// } -// -// protected class ChunkStatusWrap extends ChunkStatusWrapper { -// -// private final ChunkStatus chunkStatus; -// -// public ChunkStatusWrap(ChunkStatus chunkStatus) { -// this.chunkStatus = chunkStatus; -// } -// -// @Override -// public int requiredNeighborChunkRadius() { -// return chunkStatus.getRange(); -// } -// -// @Override -// public String name() { -// return chunkStatus.getName(); -// } -// -// @Override -// public CompletableFuture processChunk(Long xz, List accessibleChunks) { -// return chunkStatus.generate( -// Runnable::run, // TODO revisit, we might profit from this somehow? -// freshWorld, -// chunkGenerator, -// structureManager, -// threadedLevelLightEngine, -// c -> CompletableFuture.completedFuture(Either.left(c)), -// accessibleChunks -// ); -// } -// -// } -// -//} +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1.regen; + +import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.core.Fawe; +import com.fastasyncworldedit.core.queue.IChunkCache; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.util.ReflectionUtils; +import com.fastasyncworldedit.core.util.TaskManager; +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R1.PaperweightAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R1.PaperweightGetBlocks; +import com.sk89q.worldedit.extent.Extent; +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 net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +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.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.FlatLevelSource; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +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.templatesystem.StructureManager; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_18_R1.CraftServer; +import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R1.generator.CustomChunkGenerator; +import org.bukkit.generator.BlockPopulator; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public class PaperweightRegen extends Regenerator { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private static final Field serverWorldsField; + private static final Field worldPaperConfigField; + private static final Field flatBedrockField; + private static final Field generatorSettingFlatField; + private static final Field generatorSettingBaseSupplierField; + private static final Field delegateField; + private static final Field chunkProviderField; + + //list of chunk stati in correct order without FULL + private static final Map chunkStati = new LinkedHashMap<>(); + + static { + chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing + chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps + chunkStati.put( + ChunkStatus.STRUCTURE_REFERENCES, + Concurrency.FULL + ); // structure refs: radius 8, but only writes to current chunk + chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0 + chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8 + chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE + chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results + chunkStati.put( + ChunkStatus.LIQUID_CARVERS, + Concurrency.NONE + ); // liquid carvers: radius 0, but RADIUS and FULL change results + chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps + chunkStati.put( + ChunkStatus.LIGHT, + Concurrency.FULL + ); // light: radius 1, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0 + chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0 + + try { + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + Field tmpPaperConfigField; + Field tmpFlatBedrockField; + try { //only present on paper + tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); + tmpPaperConfigField.setAccessible(true); + + tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); + tmpFlatBedrockField.setAccessible(true); + } catch (Exception e) { + tmpPaperConfigField = null; + tmpFlatBedrockField = null; + } + worldPaperConfigField = tmpPaperConfigField; + flatBedrockField = tmpFlatBedrockField; + + generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( + "settings", "f")); + generatorSettingBaseSupplierField.setAccessible(true); + + generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "e")); + generatorSettingFlatField.setAccessible(true); + + delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); + delegateField.setAccessible(true); + + chunkProviderField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "L")); + chunkProviderField.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + //runtime + private ServerLevel originalServerWorld; + private ServerChunkCache originalChunkProvider; + private ServerLevel freshWorld; + private ServerChunkCache freshChunkProvider; + private LevelStorageSource.LevelStorageAccess session; + private StructureManager structureManager; + private ThreadedLevelLightEngine threadedLevelLightEngine; + private ChunkGenerator chunkGenerator; + + private Path tempDir; + + private boolean generateFlatBedrock = false; + + public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { + super(originalBukkitWorld, region, target, options); + } + + @Override + protected boolean prepare() { + this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); + originalChunkProvider = originalServerWorld.getChunkSource(); + if (!(originalChunkProvider instanceof ServerChunkCache)) { + return false; + } + + //flat bedrock? (only on paper) + if (worldPaperConfigField != null) { + try { + generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalServerWorld)); + } catch (Exception ignored) { + } + } + + seed = options.getSeed().orElse(originalServerWorld.getSeed()); + chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c)); + + return true; + } + + @Override + protected boolean initNewWorld() throws Exception { + //world folder + tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); + + //prepare for world init (see upstream implementation for reference) + org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); + org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); + LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); + ResourceKey levelStemResourceKey = getWorldDimKey(environment); + session = levelStorageSource.createAccess("worldeditregentempworld", levelStemResourceKey); + PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData; + + MinecraftServer server = originalServerWorld.getCraftServer().getServer(); + PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData(); + WorldGenSettings originalOpts = levelProperties.worldGenSettings(); + WorldGenSettings newOpts = options.getSeed().isPresent() + ? PaperweightAdapter.replaceSeed(originalServerWorld, seed, originalOpts) + : originalOpts; + LevelSettings newWorldSettings = new LevelSettings( + "worldeditregentempworld", + originalWorldData.settings.gameType(), + originalWorldData.settings.hardcore(), + originalWorldData.settings.difficulty(), + originalWorldData.settings.allowCommands(), + originalWorldData.settings.gameRules(), + originalWorldData.settings.getDataPackConfig() + ); + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable()); + + //init world + freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( + server, + server.executor, + session, + newWorldData, + originalServerWorld.dimension(), + originalServerWorld.dimensionType(), + new RegenNoOpWorldLoadListener(), + // placeholder. Required for new ChunkProviderServer, but we create and then set it later + newOpts.dimensions().get(levelStemResourceKey).generator(), + originalServerWorld.isDebug(), + seed, + ImmutableList.of(), + false, + environment, + generator, + originalBukkitWorld.getBiomeProvider() + ) { + private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse( + options + .getBiomeType() + .getId())) : null; + + @Override + public void tick(BooleanSupplier shouldKeepTicking) { //no ticking + } + + @Override + public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + if (options.hasBiomeType()) { + return singleBiome; + } + return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ, + PaperweightRegen.this.chunkGenerator.climateSampler() + ); + } + }).get(); + freshWorld.noSave = true; + removeWorldFromWorldsMap(); + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + if (worldPaperConfigField != null) { + worldPaperConfigField.set(freshWorld, originalServerWorld.paperConfig); + } + + //generator + if (originalChunkProvider.getGenerator() instanceof FlatLevelSource flatLevelSource) { + FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings(); + chunkGenerator = new FlatLevelSource(generatorSettingFlat); + } else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { + Supplier generatorSettingBaseSupplier = (Supplier) generatorSettingBaseSupplierField + .get(originalChunkProvider.getGenerator()); + BiomeSource biomeSource = originalChunkProvider.getGenerator().getBiomeSource(); + chunkGenerator = new NoiseBasedChunkGenerator(noiseBasedChunkGenerator.noises, biomeSource, seed, + generatorSettingBaseSupplier + ); + } else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator customChunkGenerator) { + chunkGenerator = customChunkGenerator.delegate; + } else { + LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName()); + return false; + } + if (generator != null) { + chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); + generateConcurrent = generator.isParallelCapable(); + } + + freshChunkProvider = new ServerChunkCache( + freshWorld, + session, + server.getFixerUpper(), + server.getStructureManager(), + server.executor, + chunkGenerator, + freshWorld.spigotConfig.viewDistance, + freshWorld.spigotConfig.simulationDistance, + server.forceSynchronousWrites(), + new RegenNoOpWorldLoadListener(), + (chunkCoordIntPair, state) -> { + }, + () -> server.overworld().getDataStorage() + ) { + // redirect to LevelChunks created in #createChunks + @Override + public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean flag) { + return getChunkAt(x, z); + } + }; + + ReflectionUtils.unsafeSet(chunkProviderField, freshWorld, freshChunkProvider); + //let's start then + structureManager = server.getStructureManager(); + threadedLevelLightEngine = freshChunkProvider.getLightEngine(); + + return true; + } + + @Override + protected void cleanup() { + try { + session.close(); + } catch (Exception ignored) { + } + + //shutdown chunk provider + try { + Fawe.instance().getQueueHandler().sync(() -> { + try { + freshChunkProvider.close(false); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception ignored) { + } + + //remove world from server + try { + Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); + } catch (Exception ignored) { + } + + //delete directory + try { + SafeFiles.tryHardToDeleteDir(tempDir); + } catch (Exception ignored) { + } + } + + @Override + protected ProtoChunk createProtoChunk(int x, int z) { + return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, + this.freshWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null + ); + } + + @Override + protected LevelChunk createChunk(ProtoChunk protoChunk) { + return new LevelChunk( + freshWorld, + protoChunk, + null // we don't want to add entities + ); + } + + @Override + protected ChunkStatusWrap getFullChunkStatus() { + return new ChunkStatusWrap(ChunkStatus.FULL); + } + + @Override + protected List getBlockPopulators() { + return originalServerWorld.getWorld().getPopulators(); + } + + @Override + protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { + // BlockPopulator#populate has to be called synchronously for TileEntity access + TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk())); + } + + @Override + protected IChunkCache initSourceQueueCache() { + return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { + @Override + public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { + return getChunkAt(x, z); + } + }; + } + + //util + private void removeWorldFromWorldsMap() { + Fawe.instance().getQueueHandler().sync(() -> { + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("worldeditregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { + switch (env) { + case NETHER: + return LevelStem.NETHER; + case THE_END: + return LevelStem.END; + case NORMAL: + default: + return LevelStem.OVERWORLD; + } + } + + private static class RegenNoOpWorldLoadListener implements ChunkProgressListener { + + private RegenNoOpWorldLoadListener() { + } + + @Override + public void updateSpawnPos(ChunkPos spawnPos) { + } + + @Override + public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { + } + + @Override + public void start() { + + } + + @Override + public void stop() { + } + + // TODO Paper only(?) @Override + public void setChunkRadius(int radius) { + } + + } + + private class FastProtoChunk extends ProtoChunk { + + public FastProtoChunk( + final ChunkPos pos, + final UpgradeData upgradeData, + final LevelHeightAccessor world, + final Registry biomeRegistry, + @Nullable final BlendingData blendingData + ) { + super(pos, upgradeData, world, biomeRegistry, blendingData); + } + + // avoid warning on paper + + // compatibility with spigot + + public boolean generateFlatBedrock() { + return generateFlatBedrock; + } + + // no one will ever see the entities! + @Override + public List getEntities() { + return Collections.emptyList(); + } + + } + + protected class ChunkStatusWrap extends ChunkStatusWrapper { + + private final ChunkStatus chunkStatus; + + public ChunkStatusWrap(ChunkStatus chunkStatus) { + this.chunkStatus = chunkStatus; + } + + @Override + public int requiredNeighborChunkRadius() { + return chunkStatus.getRange(); + } + + @Override + public String name() { + return chunkStatus.getName(); + } + + @Override + public CompletableFuture processChunk(Long xz, List accessibleChunks) { + return chunkStatus.generate( + Runnable::run, // TODO revisit, we might profit from this somehow? + freshWorld, + chunkGenerator, + structureManager, + threadedLevelLightEngine, + c -> CompletableFuture.completedFuture(Either.left(c)), + accessibleChunks, + true + ); + } + + } + +} diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index 58fd32512..f08c76ee7 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -5,7 +5,9 @@ import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent; import com.fastasyncworldedit.core.util.MathMan; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -14,6 +16,8 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -32,6 +36,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -283,19 +288,64 @@ public abstract class Regenerator biome)); + } else if (genbiomes) { + target.setBlocks(region, new WithBiomePlacementPattern(vec -> source.getBiome(vec))); + } + } + + // FIXME this shouldn't be needed + private BlockStateHolder avoidReserved(BaseBlock baseBlock) { + if (baseBlock.getBlockType() == BlockTypes.__RESERVED__) { + return BlockTypes.AIR.getDefaultState(); + } + return baseBlock; + } + + private class PlacementPattern implements Pattern { + + @Override + public BaseBlock applyBlock(final BlockVector3 position) { + return source.getFullBlock(position); + } + + @Override + public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException { + return extent.setBlock(set.getX(), set.getY(), set.getZ(), + avoidReserved(source.getFullBlock(get.getX(), get.getY(), get.getZ())) + ); + } + + } + + private class WithBiomePlacementPattern implements Pattern { + + private final Function biomeGetter; + + private WithBiomePlacementPattern(final Function biomeGetter) { + this.biomeGetter = biomeGetter; + } + + @Override + public BaseBlock applyBlock(final BlockVector3 position) { + return source.getFullBlock(position); + } + + @Override + public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException { + return extent.setBlock(set.getX(), set.getY(), set.getZ(), + avoidReserved(source.getFullBlock(get.getX(), get.getY(), get.getZ())) + ) + && extent.setBiome(set.getX(), set.getY(), set.getZ(), biomeGetter.apply(get)); + } + } //functions to be implemented by sub class