From e8c7d67b5b960dcc1fe60c4e41104141905b6a40 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 14 Sep 2024 10:48:59 +0200 Subject: [PATCH] Fix regen on modern versions (#2881) * work on regen * simplify * fix more regen * cleanup, backport * revert unneeded change --- .../fawe/v1_20_R2/regen/PaperweightRegen.java | 410 +++------------ .../fawe/v1_20_R3/regen/PaperweightRegen.java | 409 +++------------ .../fawe/v1_20_R4/regen/PaperweightRegen.java | 384 ++------------ .../fawe/v1_21_R1/PaperweightFaweAdapter.java | 3 +- .../fawe/v1_21_R1/regen/PaperweightRegen.java | 413 ++------------- .../bukkit/adapter/Regenerator.java | 496 +----------------- 6 files changed, 231 insertions(+), 1884 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java index 2ec8e6e41b..e892c6f7e0 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java @@ -4,167 +4,74 @@ 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.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache; import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.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 it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message; -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.thread.ProcessorHandle; -import net.minecraft.util.thread.ProcessorMailbox; +import net.minecraft.util.ProgressListener; 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.FixedBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; 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.WorldOptions; -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; -import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; -import org.bukkit.Chunk; +import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R2.CraftServer; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R2.generator.CustomChunkGenerator; import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; 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.OptionalLong; -import java.util.Random; -import java.util.concurrent.CompletableFuture; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightRegen extends Regenerator { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //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; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -172,43 +79,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -254,8 +155,10 @@ protected boolean initNewWorld() throws Exception { session, newWorldData, originalServerWorld.dimension(), - DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() - .getOrThrow(levelStemResourceKey), + new LevelStem( + originalServerWorld.dimensionTypeRegistration(), + originalServerWorld.getChunkSource().getGenerator() + ), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -273,17 +176,30 @@ protected boolean initNewWorld() throws Exception { ) : null; @Override - public void tick(BooleanSupplier shouldKeepTicking) { //no ticking - } - - @Override - public Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @Override + public void save( + @org.jetbrains.annotations.Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled + ) { + // noop, spigot + } + + @Override + public void save( + @Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled, + final boolean close + ) { + // noop, paper } }).get(); freshWorld.noSave = true; @@ -292,89 +208,6 @@ biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } - - ChunkGenerator originalGenerator = originalChunkProvider.getGenerator(); - if (originalGenerator instanceof FlatLevelSource flatLevelSource) { - FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings(); - chunkGenerator = new FlatLevelSource(generatorSettingFlat); - } else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { - Holder generatorSettingBaseSupplier = (Holder) generatorSettingBaseSupplierField.get( - originalGenerator); - BiomeSource biomeSource; - if (options.hasBiomeType()) { - - biomeSource = new FixedBiomeSource( - DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) - ); - } else { - biomeSource = originalGenerator.getBiomeSource(); - } - chunkGenerator = new NoiseBasedChunkGenerator( - biomeSource, - generatorSettingBaseSupplier - ); - } else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) { - chunkGenerator = customChunkGenerator.getDelegate(); - } else { - LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName()); - return false; - } - if (generator != null) { - chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); - generateConcurrent = generator.isParallelCapable(); - } -// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed - - 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 create) { - ChunkAccess chunkAccess = getChunkAt(x, z); - if (chunkAccess == null && create) { - chunkAccess = createChunk(getProtoChunkAt(x, z)); - } - return chunkAccess; - } - }; - - if (seed == originalOpts.seed() && !options.hasBiomeType()) { - // Optimisation for needless ring position calculation when the seed and biome is the same. - ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap); - boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state); - if (hasGeneratedPositions) { - Map>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> copy = new Object2ObjectArrayMap<>( - origPositions); - ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap); - ringPositionsField.set(newState, copy); - hasGeneratedPositionsField.setBoolean(newState, true); - } - } - - - chunkSourceField.set(freshWorld, freshChunkProvider); - //let's start then - structureTemplateManager = server.getStructureManager(); - threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); - return true; } @@ -389,7 +222,8 @@ protected void cleanup() { try { Fawe.instance().getQueueHandler().sync(() -> { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -410,63 +244,20 @@ protected void cleanup() { } } - @Override - protected ProtoChunk createProtoChunk(int x, int z) { - return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, - this.freshWorld.registryAccess().registryOrThrow(BIOME), 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(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @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); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { - Fawe.instance().getQueueHandler().sync(() -> { - try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); - map.remove("faweregentempworld"); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("faweregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { @@ -483,11 +274,15 @@ private RegenNoOpWorldLoadListener() { } @Override - public void updateSpawnPos(ChunkPos spawnPos) { + public void updateSpawnPos(@NotNull ChunkPos spawnPos) { } @Override - public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { + public void onStatusChange( + final @NotNull ChunkPos pos, + @org.jetbrains.annotations.Nullable final ChunkStatus status + ) { + } @Override @@ -505,87 +300,4 @@ 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.toString(); - } - - @Override - public CompletableFuture processChunk(List accessibleChunks) { - return chunkStatus.generate( - Runnable::run, // TODO revisit, we might profit from this somehow? - freshWorld, - chunkGenerator, - structureTemplateManager, - threadedLevelLightEngine, - c -> CompletableFuture.completedFuture(Either.left(c)), - accessibleChunks - ); - } - - } - - /** - * A light engine that does nothing. As light is calculated after pasting anyway, we can avoid - * work this way. - */ - static class NoOpLightEngine extends ThreadedLevelLightEngine { - - private static final ProcessorMailbox MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public CompletableFuture lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java index edf9e9f90d..cce14ee1ca 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java @@ -4,166 +4,74 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache; import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.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 it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message; -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.thread.ProcessorHandle; -import net.minecraft.util.thread.ProcessorMailbox; +import net.minecraft.util.ProgressListener; 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.FixedBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; 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.WorldOptions; -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; -import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; -import org.bukkit.Chunk; +import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R3.CraftServer; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator; import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; 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.OptionalLong; -import java.util.Random; -import java.util.concurrent.CompletableFuture; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightRegen extends Regenerator { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //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; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -171,43 +79,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -253,8 +155,10 @@ protected boolean initNewWorld() throws Exception { session, newWorldData, originalServerWorld.dimension(), - DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() - .getOrThrow(levelStemResourceKey), + new LevelStem( + originalServerWorld.dimensionTypeRegistration(), + originalServerWorld.getChunkSource().getGenerator() + ), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -272,17 +176,30 @@ protected boolean initNewWorld() throws Exception { ) : null; @Override - public void tick(BooleanSupplier shouldKeepTicking) { //no ticking - } - - @Override - public Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @Override + public void save( + @org.jetbrains.annotations.Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled + ) { + // noop, spigot + } + + @Override + public void save( + @Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled, + final boolean close + ) { + // noop, paper } }).get(); freshWorld.noSave = true; @@ -291,89 +208,6 @@ biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } - - ChunkGenerator originalGenerator = originalChunkProvider.getGenerator(); - if (originalGenerator instanceof FlatLevelSource flatLevelSource) { - FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings(); - chunkGenerator = new FlatLevelSource(generatorSettingFlat); - } else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { - Holder generatorSettingBaseSupplier = (Holder) generatorSettingBaseSupplierField.get( - originalGenerator); - BiomeSource biomeSource; - if (options.hasBiomeType()) { - - biomeSource = new FixedBiomeSource( - DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) - ); - } else { - biomeSource = originalGenerator.getBiomeSource(); - } - chunkGenerator = new NoiseBasedChunkGenerator( - biomeSource, - generatorSettingBaseSupplier - ); - } else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) { - chunkGenerator = customChunkGenerator.getDelegate(); - } else { - LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName()); - return false; - } - if (generator != null) { - chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); - generateConcurrent = generator.isParallelCapable(); - } -// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed - - 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 create) { - ChunkAccess chunkAccess = getChunkAt(x, z); - if (chunkAccess == null && create) { - chunkAccess = createChunk(getProtoChunkAt(x, z)); - } - return chunkAccess; - } - }; - - if (seed == originalOpts.seed() && !options.hasBiomeType()) { - // Optimisation for needless ring position calculation when the seed and biome is the same. - ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap); - boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state); - if (hasGeneratedPositions) { - Map>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> copy = new Object2ObjectArrayMap<>( - origPositions); - ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap); - ringPositionsField.set(newState, copy); - hasGeneratedPositionsField.setBoolean(newState, true); - } - } - - - chunkSourceField.set(freshWorld, freshChunkProvider); - //let's start then - structureTemplateManager = server.getStructureManager(); - threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); - return true; } @@ -388,7 +222,8 @@ protected void cleanup() { try { Fawe.instance().getQueueHandler().sync(() -> { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -409,63 +244,20 @@ protected void cleanup() { } } - @Override - protected ProtoChunk createProtoChunk(int x, int z) { - return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, - this.freshWorld.registryAccess().registryOrThrow(BIOME), 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(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @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); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @SuppressWarnings("unchecked") private void removeWorldFromWorldsMap() { - Fawe.instance().getQueueHandler().sync(() -> { - try { - Map map = (Map) serverWorldsField.get(Bukkit.getServer()); - map.remove("faweregentempworld"); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("faweregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { @@ -482,11 +274,15 @@ private RegenNoOpWorldLoadListener() { } @Override - public void updateSpawnPos(ChunkPos spawnPos) { + public void updateSpawnPos(@NotNull ChunkPos spawnPos) { } @Override - public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { + public void onStatusChange( + final @NotNull ChunkPos pos, + @org.jetbrains.annotations.Nullable final ChunkStatus status + ) { + } @Override @@ -504,87 +300,4 @@ 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.toString(); - } - - @Override - public CompletableFuture processChunk(List accessibleChunks) { - return chunkStatus.generate( - Runnable::run, // TODO revisit, we might profit from this somehow? - freshWorld, - chunkGenerator, - structureTemplateManager, - threadedLevelLightEngine, - c -> CompletableFuture.completedFuture(Either.left(c)), - accessibleChunks - ); - } - - } - - /** - * A light engine that does nothing. As light is calculated after pasting anyway, we can avoid - * work this way. - */ - static class NoOpLightEngine extends ThreadedLevelLightEngine { - - private static final ProcessorMailbox MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public CompletableFuture lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java index e21e7eef73..d80d098ffd 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/regen/PaperweightRegen.java @@ -4,166 +4,73 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R4.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 it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message; -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.thread.ProcessorHandle; -import net.minecraft.util.thread.ProcessorMailbox; +import net.minecraft.util.ProgressListener; 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.FixedBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -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.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.WorldGenContext; 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.WorldOptions; -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; -import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; -import org.bukkit.Chunk; +import org.bukkit.World; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.generator.CustomChunkGenerator; import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; 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.OptionalLong; -import java.util.Random; -import java.util.concurrent.CompletableFuture; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightRegen extends Regenerator { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //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.NONE - ); // structure refs: radius 8, but only writes to current chunk - chunkStati.put(ChunkStatus.BIOMES, Concurrency.NONE); // 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.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps - chunkStati.put( - ChunkStatus.INITIALIZE_LIGHT, - Concurrency.FULL - ); // initialize_light: radius 0 - chunkStati.put( - ChunkStatus.LIGHT, - Concurrency.FULL - ); // light: radius 1, but no writes to other chunks, only current chunk - chunkStati.put(ChunkStatus.SPAWN, Concurrency.NONE); // spawn: 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; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -171,44 +78,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -254,8 +154,10 @@ protected boolean initNewWorld() throws Exception { session, newWorldData, originalServerWorld.dimension(), - DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() - .getOrThrow(levelStemResourceKey), + new LevelStem( + originalServerWorld.dimensionTypeRegistration(), + originalServerWorld.getChunkSource().getGenerator() + ), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -272,20 +174,32 @@ protected boolean initNewWorld() throws Exception { WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) ) : null; - @Override - public void tick(@NotNull BooleanSupplier shouldKeepTicking) { //no ticking - } - @Override public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @Override + public void save( + @org.jetbrains.annotations.Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled + ) { + // noop, spigot } + @Override + public void save( + @Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled, + final boolean close + ) { + // noop, paper + } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); @@ -293,93 +207,6 @@ biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } - - ChunkGenerator originalGenerator = originalChunkProvider.getGenerator(); - if (originalGenerator instanceof FlatLevelSource flatLevelSource) { - FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings(); - chunkGenerator = new FlatLevelSource(generatorSettingFlat); - } else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { - Holder generatorSettingBaseSupplier = (Holder) - generatorSettingBaseSupplierField.get(noiseBasedChunkGenerator); - BiomeSource biomeSource; - if (options.hasBiomeType()) { - biomeSource = new FixedBiomeSource( - DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) - ); - } else { - biomeSource = originalGenerator.getBiomeSource(); - } - chunkGenerator = new NoiseBasedChunkGenerator( - biomeSource, - generatorSettingBaseSupplier - ); - } else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) { - chunkGenerator = customChunkGenerator.getDelegate(); - } else { - LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName()); - return false; - } - if (generator != null) { - chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); - generateConcurrent = generator.isParallelCapable(); - } -// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed - - 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, @NotNull ChunkStatus chunkstatus, boolean create) { - ChunkAccess chunkAccess = getChunkAt(x, z); - if (chunkAccess == null && create) { - chunkAccess = createChunk(getProtoChunkAt(x, z)); - } - return chunkAccess; - } - }; - - if (seed == originalOpts.seed() && !options.hasBiomeType()) { - // Optimisation for needless ring position calculation when the seed and biome is the same. - ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get( - originalChunkProvider.chunkMap); - boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state); - if (hasGeneratedPositions) { - Map>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> copy = new Object2ObjectArrayMap<>( - origPositions); - ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get( - freshChunkProvider.chunkMap); - ringPositionsField.set(newState, copy); - hasGeneratedPositionsField.setBoolean(newState, true); - } - } - - - chunkSourceField.set(freshWorld, freshChunkProvider); - //let's start then - structureTemplateManager = server.getStructureManager(); - threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); - - this.worldGenContext = new WorldGenContext(freshWorld, chunkGenerator, structureTemplateManager, - threadedLevelLightEngine - ); return true; } @@ -394,7 +221,8 @@ protected void cleanup() { try { Fawe.instance().getQueueHandler().sync(() -> { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -415,50 +243,9 @@ protected void cleanup() { } } - @Override - protected ProtoChunk createProtoChunk(int x, int z) { - return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, - this.freshWorld.registryAccess().registryOrThrow(BIOME), 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(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @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); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @@ -512,83 +299,4 @@ 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 - - @SuppressWarnings("unused") // compatibility with spigot - public boolean generateFlatBedrock() { - return generateFlatBedrock; - } - - // no one will ever see the entities! - @Override - public @NotNull 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.toString(); - } - - @Override - public CompletableFuture processChunk(List accessibleChunks) { - return chunkStatus.generate( - worldGenContext, - Runnable::run, - CompletableFuture::completedFuture, - accessibleChunks - ); - } - - } - - /** - * A light engine that does nothing. As light is calculated after pasting anyway, we can avoid - * work this way. - */ - static class NoOpLightEngine extends ThreadedLevelLightEngine { - - private static final ProcessorMailbox MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public @NotNull CompletableFuture lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java index 9452024c5e..34fc70f8de 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java @@ -19,6 +19,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.nbt.PaperweightLazyCompoundTag; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen.PaperweightRegen; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -565,7 +566,7 @@ public net.minecraft.nbt.Tag fromNative(Tag foreign) { @Override public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { - throw new UnsupportedOperationException("Regen support for 1.21 not yet implemented."); + return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java index 07933adfa8..f62ae8e5d2 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/regen/PaperweightRegen.java @@ -1,175 +1,76 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_R1.regen; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; 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.TaskManager; +import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_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 it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message; -import net.minecraft.server.level.GenerationChunkHolder; -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.StaticCache2D; -import net.minecraft.util.thread.ProcessorHandle; -import net.minecraft.util.thread.ProcessorMailbox; +import net.minecraft.util.ProgressListener; 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.FixedBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -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.chunk.status.ChunkPyramid; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.WorldGenContext; 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.WorldOptions; -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; -import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; -import org.bukkit.Chunk; +import org.bukkit.World; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.generator.CustomChunkGenerator; import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; 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.OptionalLong; -import java.util.Random; -import java.util.concurrent.CompletableFuture; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import static net.minecraft.core.registries.Registries.BIOME; -public class PaperweightRegen extends Regenerator { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public class PaperweightRegen extends Regenerator { private static final Field serverWorldsField; private static final Field paperConfigField; - private static final Field flatBedrockField; - private static final Field generatorSettingFlatField; private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - private static final Field generatorStructureStateField; - private static final Field ringPositionsField; - private static final Field hasGeneratedPositionsField; - //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.NONE - ); // structure refs: radius 8, but only writes to current chunk - chunkStati.put(ChunkStatus.BIOMES, Concurrency.NONE); // 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.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps - chunkStati.put( - ChunkStatus.INITIALIZE_LIGHT, - Concurrency.FULL - ); // initialize_light: radius 0 - chunkStati.put( - ChunkStatus.LIGHT, - Concurrency.FULL - ); // light: radius 1, but no writes to other chunks, only current chunk - chunkStati.put(ChunkStatus.SPAWN, Concurrency.NONE); // spawn: 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; } paperConfigField = tmpPaperConfigField; - flatBedrockField = tmpFlatBedrockField; generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( "settings", "e")); generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); - chunkSourceField.setAccessible(true); - - generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "w")); - generatorStructureStateField.setAccessible(true); - - ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); - ringPositionsField.setAccessible(true); - - hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( - Refraction.pickName("hasGeneratedPositions", "h") - ); - hasGeneratedPositionsField.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } @@ -177,47 +78,37 @@ public class PaperweightRegen extends Regenerator super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - return true; } @Override - @SuppressWarnings("unchecked") protected boolean initNewWorld() throws Exception { //world folder tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); @@ -263,8 +154,10 @@ protected boolean initNewWorld() throws Exception { session, newWorldData, originalServerWorld.dimension(), - DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() - .getOrThrow(levelStemResourceKey), + new LevelStem( + originalServerWorld.dimensionTypeRegistration(), + originalServerWorld.getChunkSource().getGenerator() + ), new RegenNoOpWorldLoadListener(), originalServerWorld.isDebug(), seed, @@ -281,20 +174,32 @@ protected boolean initNewWorld() throws Exception { WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) ) : null; - @Override - public void tick(@NotNull BooleanSupplier shouldKeepTicking) { //no ticking - } - @Override public @NotNull Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { if (options.hasBiomeType()) { return singleBiome; } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( - biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() - ); + return super.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @Override + public void save( + @Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled + ) { + // noop, spigot } + @Override + public void save( + @Nullable final ProgressListener progressListener, + final boolean flush, + final boolean savingDisabled, + final boolean close + ) { + // noop, paper + } }).get(); freshWorld.noSave = true; removeWorldFromWorldsMap(); @@ -302,97 +207,6 @@ biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() if (paperConfigField != null) { paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); } - - ChunkGenerator originalGenerator = originalChunkProvider.getGenerator(); - if (originalGenerator instanceof FlatLevelSource flatLevelSource) { - FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings(); - chunkGenerator = new FlatLevelSource(generatorSettingFlat); - } else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { - Holder generatorSettingBaseSupplier = (Holder) - generatorSettingBaseSupplierField.get(noiseBasedChunkGenerator); - BiomeSource biomeSource; - if (options.hasBiomeType()) { - biomeSource = new FixedBiomeSource( - DedicatedServer.getServer().registryAccess() - .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( - WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) - ) - ); - } else { - biomeSource = originalGenerator.getBiomeSource(); - } - chunkGenerator = new NoiseBasedChunkGenerator( - biomeSource, - generatorSettingBaseSupplier - ); - } else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) { - chunkGenerator = customChunkGenerator.getDelegate(); - } else { - LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName()); - return false; - } - if (generator != null) { - chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); - generateConcurrent = generator.isParallelCapable(); - } -// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed - - 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, @NotNull ChunkStatus chunkstatus, boolean create) { - ChunkAccess chunkAccess = getChunkAt(x, z); - if (chunkAccess == null && create) { - chunkAccess = createChunk(getProtoChunkAt(x, z)); - } - return chunkAccess; - } - }; - - if (seed == originalOpts.seed() && !options.hasBiomeType()) { - // Optimisation for needless ring position calculation when the seed and biome is the same. - ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get( - originalChunkProvider.chunkMap); - boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state); - if (hasGeneratedPositions) { - Map>> origPositions = - (Map>>) ringPositionsField.get(state); - Map>> copy = new Object2ObjectArrayMap<>( - origPositions); - ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get( - freshChunkProvider.chunkMap); - ringPositionsField.set(newState, copy); - hasGeneratedPositionsField.setBoolean(newState, true); - } - } - - - chunkSourceField.set(freshWorld, freshChunkProvider); - //let's start then - structureTemplateManager = server.getStructureManager(); - threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); - - this.worldGenContext = new WorldGenContext( - freshWorld, - chunkGenerator, - structureTemplateManager, - threadedLevelLightEngine, - originalChunkProvider.chunkMap.worldGenContext.mainThreadMailBox() - ); return true; } @@ -407,7 +221,8 @@ protected void cleanup() { try { Fawe.instance().getQueueHandler().sync(() -> { try { - freshChunkProvider.close(false); + freshWorld.getChunkSource().getDataStorage().cache.clear(); + freshWorld.getChunkSource().close(false); } catch (Exception e) { throw new RuntimeException(e); } @@ -428,50 +243,9 @@ protected void cleanup() { } } - @Override - protected ProtoChunk createProtoChunk(int x, int z) { - return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, - this.freshWorld.registryAccess().registryOrThrow(BIOME), 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(() -> { - final CraftWorld world = freshWorld.getWorld(); - final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); - blockPopulator.populate(world, random, chunk); - }); - } - @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); - } - }; + return new ChunkCache<>(BukkitAdapter.adapt(freshWorld.getWorld())); } //util @@ -525,99 +299,4 @@ 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 - - @SuppressWarnings("unused") // compatibility with spigot - public boolean generateFlatBedrock() { - return generateFlatBedrock; - } - - // no one will ever see the entities! - @Override - public @NotNull 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 ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL).getAccumulatedRadiusOf(chunkStatus); - } - - @Override - public String name() { - return chunkStatus.toString(); - } - - @Override - public CompletableFuture processChunk(List accessibleChunks) { - ChunkAccess chunkAccess = accessibleChunks.get(accessibleChunks.size() / 2); - int chunkX = chunkAccess.getPos().x; - int chunkZ = chunkAccess.getPos().z; - getProtoChunkAt(chunkX, chunkZ); - StaticCache2D neighbours = StaticCache2D - .create( - chunkX, - chunkZ, - requiredNeighborChunkRadius(), - (final int nx, final int nz) -> new ChunkHolder(new ChunkPos(nx, nz), - ChunkHolderManager.MAX_TICKET_LEVEL, - freshWorld, - threadedLevelLightEngine, - null, - freshChunkProvider.chunkMap - ) - ); - return ChunkPyramid.GENERATION_PYRAMID.getStepTo(chunkStatus).apply( - worldGenContext, - neighbours, - chunkAccess - ); - } - - } - - /** - * A light engine that does nothing. As light is calculated after pasting anyway, we can avoid - * work this way. - */ - static class NoOpLightEngine extends ThreadedLevelLightEngine { - - private static final ProcessorMailbox MAILBOX = ProcessorMailbox.create(task -> { - }, "fawe-no-op"); - private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { - }); - - public NoOpLightEngine(final ServerChunkCache chunkProvider) { - super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); - } - - @Override - public @NotNull CompletableFuture lightChunk(final @NotNull ChunkAccess chunk, final boolean excludeBlocks) { - return CompletableFuture.completedFuture(chunk); - } - - } - } 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 4afbe03a2d..ac75a29f62 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 @@ -1,67 +1,32 @@ package com.fastasyncworldedit.bukkit.adapter; -import com.fastasyncworldedit.core.configuration.Settings; 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.google.common.collect.Lists; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.fastasyncworldedit.core.util.TaskManager; 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; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.math.BlockVector2; 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 com.sk89q.worldedit.world.block.BaseBlock; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.longs.LongList; -import jdk.jfr.Category; -import jdk.jfr.Event; -import jdk.jfr.Label; -import jdk.jfr.Name; -import org.apache.logging.log4j.Logger; import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.WorldInfo; -import java.util.AbstractList; -import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; import java.util.function.Function; /** * Represents an abstract regeneration handler. - * - * @param the type of the {@code IChunkAccess} of the current Minecraft implementation - * @param the type of the {@code ProtoChunk} of the current Minecraft implementation - * @param the type of the {@code Chunk} of the current Minecraft implementation - * @param the type of the {@code ChunkStatusWrapper} wrapping the {@code ChunkStatus} enum */ -public abstract class Regenerator> { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); +public abstract class Regenerator { protected final org.bukkit.World originalBukkitWorld; protected final Region region; @@ -69,13 +34,8 @@ public abstract class Regenerator chunkStatuses = new LinkedHashMap<>(); // TODO (j21): use SequencedMap - private final Long2ObjectLinkedOpenHashMap protoChunks = new Long2ObjectLinkedOpenHashMap<>(); - private final Long2ObjectOpenHashMap chunks = new Long2ObjectOpenHashMap<>(); - protected boolean generateConcurrent = true; protected long seed; - private ExecutorService executor; - private SingleThreadQueueExtent source; + protected SingleThreadQueueExtent source; /** * Initializes an abstract regeneration handler. @@ -92,15 +52,6 @@ public Regenerator(org.bukkit.World originalBukkitWorld, Region region, Extent t this.options = options; } - private static Random getChunkRandom(long worldSeed, int x, int z) { - Random random = new Random(); - random.setSeed(worldSeed); - long xRand = random.nextLong() / 2L * 2L + 1L; - long zRand = random.nextLong() / 2L * 2L + 1L; - random.setSeed((long) x * xRand + (long) z * zRand ^ worldSeed); - return random; - } - /** * Regenerates the selected {@code Region}. * @@ -122,16 +73,6 @@ public boolean regenerate() throws Exception { throw e; } - try { - if (!generate()) { - cleanup0(); - return false; - } - } catch (Exception e) { - cleanup0(); - throw e; - } - try { copyToWorld(); } catch (Exception e) { @@ -144,193 +85,26 @@ public boolean regenerate() throws Exception { } /** - * Returns the {@code ProtoChunk} at the given chunk coordinates. - * - * @param x the chunk x coordinate - * @param z the chunk z coordinate - * @return the {@code ProtoChunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been initialized yet. - */ - protected ProtoChunk getProtoChunkAt(int x, int z) { - return protoChunks.get(MathMan.pairInt(x, z)); - } - - /** - * Returns the {@code Chunk} at the given chunk coordinates. - * - * @param x the chunk x coordinate - * @param z the chunk z coordinate - * @return the {@code Chunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been converted yet. + * Execute tasks on the main thread during regen. */ - protected Chunk getChunkAt(int x, int z) { - return chunks.get(MathMan.pairInt(x, z)); - } - - private boolean generate() throws Exception { - ThreadFactory factory = new ThreadFactoryBuilder() - .setNameFormat("FAWE Regenerator - %d") - .build(); - if (generateConcurrent) { - //Using concurrent chunk generation - executor = Executors.newFixedThreadPool(Settings.settings().QUEUE.PARALLEL_THREADS, factory); - } else { // else using sequential chunk generation, concurrent not supported - executor = Executors.newSingleThreadExecutor(factory); - } - - //TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)? - //for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius - - // to get the chunks we need to generate in the nth chunk status, we need to know how many chunks - // we need to generate in the n + 1 th chunk status. Summing up the margin solves that - LinkedHashMap chunkCoordsForChunkStatus = new LinkedHashMap<>(); - int borderSum = 1; - // TODO (j21): use SequencedMap#sequencedKeySet().reversed() - final List reversedKeys = Lists.reverse(new ArrayList<>(chunkStatuses.keySet())); - for (final ChunkStatus status : reversedKeys) { - chunkCoordsForChunkStatus.put(status, getChunkCoordsRegen(region, borderSum)); - borderSum += status.requiredNeighborChunkRadius(); - } - - //create chunks - // TODO (j21): use SequencedMap#firstEntry().getKey() - for (long xz : chunkCoordsForChunkStatus.get(chunkStatuses.keySet().iterator().next())) { - ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz)); - protoChunks.put(xz, chunk); - } - - // a memory-efficient, lightweight "list" that calculates index -> ChunkAccess - // as needed when accessed - class LazyChunkList extends AbstractList { - private final int size; - private final int minX; - private final int minZ; - private final int sizeSqrt; - - LazyChunkList(int radius, int centerX, int centerZ) { - this.sizeSqrt = radius + 1 + radius; // length of one side - this.size = this.sizeSqrt * this.sizeSqrt; - this.minX = centerX - radius; - this.minZ = centerZ - radius; - } - @Override - public IChunkAccess get(final int index) { - Objects.checkIndex(index, size); - int absX = (index % sizeSqrt) + minX; - int absZ = (index / sizeSqrt) + minZ; - return protoChunks.get(MathMan.pairInt(absX, absZ)); - } - - @Override - public int size() { - return size; - } - - } - @Label("Regeneration") - @Category("FAWE") - @Name("fawe.regen") - class RegenerationEvent extends Event { - private String chunkStatus; - private int chunksToProcess; - } - - //run generation tasks excluding FULL chunk status - for (Map.Entry entry : chunkStatuses.entrySet()) { - ChunkStatus chunkStatus = entry.getKey(); - final RegenerationEvent event = new RegenerationEvent(); - event.begin(); - event.chunkStatus = chunkStatus.name(); - int radius = Math.max(1, chunkStatus.requiredNeighborChunkRadius0()); - - long[] coords = chunkCoordsForChunkStatus.get(chunkStatus); - event.chunksToProcess = coords.length; - if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) { - SequentialTasks> tasks = getChunkStatusTaskRows(coords, radius); - for (ConcurrentTasks para : tasks) { - List scheduled = new ArrayList<>(tasks.size()); - for (LongList row : para) { - scheduled.add(() -> { - for (long xz : row) { - chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz), - MathMan.unpairIntY(xz))); - } - }); - } - runAndWait(scheduled); - } - } else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) { - // every chunk can be processed individually - List scheduled = new ArrayList<>(coords.length); - for (long xz : coords) { - scheduled.add(() -> chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz), - MathMan.unpairIntY(xz)))); - } - runAndWait(scheduled); - } else { // Concurrency.NONE or generateConcurrent == false - // run sequential but submit to different thread - // running regen on the main thread otherwise triggers async-only events on the main thread - executor.submit(() -> { - for (long xz : coords) { - chunkStatus.processChunkSave(xz, new LazyChunkList(radius, MathMan.unpairIntX(xz), - MathMan.unpairIntY(xz))); - } - }).get(); // wait until finished this step - } - event.commit(); - } + protected abstract void runTasks(BooleanSupplier shouldKeepTicking); - //convert to proper chunks - // TODO (j21): use SequencedMap#firstEntry().getValue() - for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) { - ProtoChunk proto = protoChunks.get(xz); - chunks.put(xz, createChunk(proto)); - } - - //final chunkstatus - ChunkStatus FULL = getFullChunkStatus(); - // TODO (j21): use SequencedMap#firstEntry().getValue() - for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) { //FULL.requiredNeighbourChunkRadius() == 0! - Chunk chunk = chunks.get(xz); - FULL.processChunkSave(xz, List.of(chunk)); - } - - //populate - List populators = getBlockPopulators(); - // TODO (j21): use SequencedMap#firstEntry().getValue() - for (long xz : chunkCoordsForChunkStatus.values().iterator().next()) { - int x = MathMan.unpairIntX(xz); - int z = MathMan.unpairIntY(xz); - - //prepare chunk seed - Random random = getChunkRandom(seed, x, z); - - //actually populate - Chunk c = chunks.get(xz); - populators.forEach(pop -> { - populate(c, random, pop); - }); - } + private void createSource() { source = new SingleThreadQueueExtent( BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0, BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256 ); source.init(target, initSourceQueueCache(), null); - return true; - } - - private void runAndWait(final List tasks) { - try { - List> futures = new ArrayList<>(); - tasks.forEach(task -> futures.add(executor.submit(task))); - for (Future future : futures) { - future.get(); - } - } catch (Exception e) { - LOGGER.catching(e); - } } private void copyToWorld() { + createSource(); + final long timeoutPerTick = TimeUnit.MILLISECONDS.toNanos(10); + int taskId = TaskManager.taskManager().repeat(() -> { + final long startTime = System.nanoTime(); + runTasks(() -> System.nanoTime() - startTime < timeoutPerTick); + }, 1); //Setting Blocks boolean genbiomes = options.shouldRegenBiomes(); boolean hasBiome = options.hasBiomeType(); @@ -343,6 +117,7 @@ private void copyToWorld() { } else if (genbiomes) { target.setBlocks(region, new WithBiomePlacementPattern(vec -> source.getBiome(vec))); } + TaskManager.taskManager().cancel(taskId); } private class PlacementPattern implements Pattern { @@ -382,9 +157,6 @@ public boolean apply(final Extent extent, final BlockVector3 get, final BlockVec //functions to be implemented by sub class private void cleanup0() { - if (executor != null) { - executor.shutdownNow(); - } cleanup(); } @@ -416,47 +188,6 @@ private void cleanup0() { */ protected abstract void cleanup(); - /** - * Implement the initialization of a {@code ProtoChunk} here. - * - * @param x the x coorinate of the {@code ProtoChunk} to create - * @param z the z coorinate of the {@code ProtoChunk} to create - * @return an initialized {@code ProtoChunk} - */ - protected abstract ProtoChunk createProtoChunk(int x, int z); - - /** - * Implement the convertion of a {@code ProtoChunk} to a {@code Chunk} here. - * - * @param protoChunk the {@code ProtoChunk} to be converted to a {@code Chunk} - * @return the converted {@code Chunk} - */ - protected abstract Chunk createChunk(ProtoChunk protoChunk); - - /** - * Return the {@code ChunkStatus.FULL} here. - * ChunkStatus.FULL is the last step of vanilla chunk generation. - * - * @return {@code ChunkStatus.FULL} - */ - protected abstract ChunkStatus getFullChunkStatus(); - - /** - * Return a list of {@code BlockPopulator} used to populate the original world here. - * - * @return {@code ChunkStatus.FULL} - */ - protected abstract List getBlockPopulators(); - - /** - * Implement the population of the {@code Chunk} with the given chunk random and {@code BlockPopulator} here. - * - * @param chunk the {@code Chunk} to populate - * @param random the chunk random to use for population - * @param pop the {@code BlockPopulator} to use - */ - protected abstract void populate(Chunk chunk, Random random, BlockPopulator pop); - /** * Implement the initialization an {@code IChunkCache} here. Use will need the {@code getChunkAt} function * @@ -464,106 +195,6 @@ private void cleanup0() { */ protected abstract IChunkCache initSourceQueueCache(); - //algorithms - private long[] getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks - BlockVector3 oldMin = region.getMinimumPoint(); - BlockVector3 newMin = BlockVector3.at( - (oldMin.x() >> 4 << 4) - border * 16, - oldMin.y(), - (oldMin.z() >> 4 << 4) - border * 16 - ); - BlockVector3 oldMax = region.getMaximumPoint(); - BlockVector3 newMax = BlockVector3.at( - (oldMax.x() >> 4 << 4) + (border + 1) * 16 - 1, - oldMax.y(), - (oldMax.z() >> 4 << 4) + (border + 1) * 16 - 1 - ); - Region adjustedRegion = new CuboidRegion(newMin, newMax); - return adjustedRegion.getChunks().stream() - .sorted(Comparator - .comparingInt(BlockVector2::z) - .thenComparingInt(BlockVector2::x)) //needed for RegionLimitedWorldAccess - .mapToLong(c -> MathMan.pairInt(c.x(), c.z())) - .toArray(); - } - - /** - * Creates a list of chunkcoord rows that may be executed concurrently - * - * @param allCoords the coords that should be sorted into rows, must be sorted by z and x - * @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus - * .requiredNeighborRadius) - * @return a list of chunkcoords rows that may be executed concurrently - */ - private SequentialTasks> getChunkStatusTaskRows( - long[] allCoords, - int requiredNeighborChunkRadius - ) { - int requiredNeighbors = Math.max(0, requiredNeighborChunkRadius); - - final int coordsCount = allCoords.length; - long first = coordsCount == 0 ? 0 : allCoords[0]; - long last = coordsCount == 0 ? 0 : allCoords[coordsCount - 1]; - int minX = MathMan.unpairIntX(first); - int maxX = MathMan.unpairIntX(last); - int minZ = MathMan.unpairIntY(first); - int maxZ = MathMan.unpairIntY(last); - SequentialTasks> tasks; - if (maxZ - minZ > maxX - minX) { - int numlists = Math.min(requiredNeighbors * 2 + 1, maxX - minX + 1); - - Int2ObjectOpenHashMap byX = new Int2ObjectOpenHashMap<>(); - int expectedListLength = (coordsCount + 1) / (maxX - minX); - - //init lists - for (int i = minX; i <= maxX; i++) { - byX.put(i, new LongArrayList(expectedListLength)); - } - - //sort into lists by x coord - for (long allCoord : allCoords) { - byX.get(MathMan.unpairIntX(allCoord)).add(allCoord); - } - - //create parallel tasks - tasks = new SequentialTasks<>(numlists); - for (int offset = 0; offset < numlists; offset++) { - ConcurrentTasks para = new ConcurrentTasks<>((maxZ - minZ + 1) / numlists + 1); - for (int i = 0; minX + i * numlists + offset <= maxX; i++) { - para.add(byX.get(minX + i * numlists + offset)); - } - tasks.add(para); - } - } else { - int numlists = Math.min(requiredNeighbors * 2 + 1, maxZ - minZ + 1); - - Int2ObjectOpenHashMap byZ = new Int2ObjectOpenHashMap<>(); - int expectedListLength = (coordsCount + 1) / (maxZ - minZ + 2); - - //init lists - for (int i = minZ; i <= maxZ; i++) { - byZ.put(i, new LongArrayList(expectedListLength)); - } - - //sort into lists by x coord - for (long allCoord : allCoords) { - byZ.get(MathMan.unpairIntY(allCoord)).add(allCoord); - } - - //create parallel tasks - tasks = new SequentialTasks<>(numlists); - for (int offset = 0; offset < numlists; offset++) { - ConcurrentTasks para = new ConcurrentTasks<>((maxX - minX + 1) / numlists + 1); - for (int i = 0; minZ + i * numlists + offset <= maxZ; i++) { - para.add(byZ.get(minZ + i * numlists + offset)); - } - tasks.add(para); - } - } - - return tasks; - } - protected BiomeProvider getBiomeProvider() { if (options.hasBiomeType()) { return new SingleBiomeProvider(); @@ -579,103 +210,6 @@ public enum Concurrency { NONE } - /** - * This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step. - * - * @param the IChunkAccess class of the current Minecraft implementation - */ - public static abstract class ChunkStatusWrapper { - - /** - * Return the required neighbor chunk radius the wrapped {@code ChunkStatus} requires. - * - * @return the radius of required neighbor chunks - */ - public abstract int requiredNeighborChunkRadius(); - - int requiredNeighborChunkRadius0() { - return Math.max(0, requiredNeighborChunkRadius()); - } - - /** - * Return the name of the wrapped {@code ChunkStatus}. - * - * @return the radius of required neighbor chunks - */ - public abstract String name(); - - /** - * Return the name of the wrapped {@code ChunkStatus}. - * - * @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}. - * This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently. - */ - public abstract CompletableFuture processChunk(List accessibleChunks); - - void processChunkSave(long xz, List accessibleChunks) { - try { - processChunk(accessibleChunks).get(); - } catch (Exception e) { - LOGGER.error("Error while running {} on chunk {}/{}", - name(), MathMan.unpairIntX(xz), MathMan.unpairIntY(xz), e); - } - } - - @Override - public String toString() { - return name(); - } - - } - - public static class SequentialTasks extends Tasks { - - public SequentialTasks(int expectedSize) { - super(expectedSize); - } - - } - - public static class ConcurrentTasks extends Tasks { - - public ConcurrentTasks(int expectedSize) { - super(expectedSize); - } - - } - - public static class Tasks implements Iterable { - - private final List tasks; - - public Tasks(int expectedSize) { - tasks = new ArrayList<>(expectedSize); - } - - public void add(T task) { - tasks.add(task); - } - - public List list() { - return tasks; - } - - public int size() { - return tasks.size(); - } - - @Override - public Iterator iterator() { - return tasks.iterator(); - } - - @Override - public String toString() { - return tasks.toString(); - } - - } - public class SingleBiomeProvider extends BiomeProvider { private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType());