Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-09 13:00:05 +01:00
Re-Implement //regen (#598)
* start reimplementing regen command * start reimplementing regen command * Formatting and logic tweaks. Regen will now throw exceptions but they are not caught yet. ConversionSessions will now be closed when no longer being used instead of left open. * fix //regen crashing server * added //regen support for 1.16.1 and 1.15.2 * cleanup * Update the issue template * improve performance of regen by a factor of 40, approx 1.2 millon blocks/second * Update the issue template * Update the issue template & add a config (#640) * Update the issue template * Add a config.yml to the issue template * improve performance of regen by a factor of 40, approx 1.2 millon blocks/second * Fix entity rotation (#642) * fix entity rotation fixes #577 Co-authored-by: wea_ondara <wea_ondara@alpenblock.net> * Fix toggle permission (#644) * Fixes #529 * fix superperms perm toggling Co-authored-by: @weaondara <wea_ondara@alpenblock.net> * Fix #647 * Squash errors and debug to aid fixing #652 properly * cleanup imports * cleanup imports 2 * cleanup imports 3 * cleanup imports 4 * add patch by @SirYwell * aysnc world gen with features and stuff and some minor issues * optimizations, full chunkstatus, block populators * optimizations, cleanup * optimizations * fix feature regeneration, fix temp folder deletion * cleanup * make chunk gen multithreaded * fix precomputation of chunk lists for RegionLimitedWorldAccess again * added regenerator abstraction, fix aioobe while running through chunk stati * remove override for getChunkAt in freshnmsworld * don't use concurrent chunk gen if custom chunk generators do not support it * distinct between generator types * improve regen speed for overworlds * mix * Add message that regen might take a while * use a shared map for FastAreaLazy, cleanup imports * use custom concurrency levels for chunk stati and process accordingly * implement new regen in 1.15.2 and 1.16.1 as well Conflicts: worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java * woops * further abstraction, finalized regen impl * Formatting * Fix some typos, remove debug * replace wildcard imports * cleanup debug * braces * serr -> logger * move regen impls to seperate classes * fix world init for 1.16.1 * fix world init for 1.15.2 * fix world init for 1.15.2 #2 * use original world name for regeneration * Update Regen_v1_15_R2.java * Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_15_R2.java Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * Update worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_16_R1.java Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * improve documentation, use parallel task count for fawe settings * fix compile Co-authored-by: wea_ondara <wea_ondara@alpenblock.net> Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com> Co-authored-by: dordsor21 <dordsor21@gmail.com>
Dieser Commit ist enthalten in:
Ursprung
d42e152348
Commit
3c6280b11e
@ -32,6 +32,7 @@ import com.sk89q.worldedit.blocks.BaseItem;
|
|||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.entity.Player;
|
import com.sk89q.worldedit.entity.Player;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||||
import com.sk89q.worldedit.math.BlockVector2;
|
import com.sk89q.worldedit.math.BlockVector2;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
@ -42,6 +43,7 @@ import com.sk89q.worldedit.util.SideEffect;
|
|||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
import com.sk89q.worldedit.util.TreeGenerator;
|
||||||
import com.sk89q.worldedit.world.AbstractWorld;
|
import com.sk89q.worldedit.world.AbstractWorld;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
@ -202,61 +204,18 @@ public class BukkitWorld extends AbstractWorld {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean regenerate(Region region, EditSession editSession) {
|
public boolean regenerate(Region region, Extent extent, RegenOptions options) {
|
||||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||||
try {
|
try {
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
return adapter.regenerate(getWorld(), region, editSession);
|
return adapter.regenerate(getWorld(), region, extent, options);
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("Missing BukkitImplAdapater for this version.");
|
throw new UnsupportedOperationException("Missing BukkitImplAdapater for this version.");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Regeneration via adapter failed.", e);
|
logger.warn("Regeneration via adapter failed.", e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)];
|
|
||||||
|
|
||||||
for (BlockVector2 chunk : region.getChunks()) {
|
|
||||||
BlockVector3 min = BlockVector3.at(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);
|
|
||||||
|
|
||||||
// First save all the blocks inside
|
|
||||||
for (int x = 0; x < 16; ++x) {
|
|
||||||
for (int y = 0; y < (getMaxY() + 1); ++y) {
|
|
||||||
for (int z = 0; z < 16; ++z) {
|
|
||||||
BlockVector3 pt = min.add(x, y, z);
|
|
||||||
int index = y * 16 * 16 + z * 16 + x;
|
|
||||||
history[index] = editSession.getFullBlock(pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
getWorld().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ());
|
|
||||||
} catch (Throwable t) {
|
|
||||||
logger.warn("Chunk generation via Bukkit raised an error", t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then restore
|
|
||||||
for (int x = 0; x < 16; ++x) {
|
|
||||||
for (int y = 0; y < (getMaxY() + 1); ++y) {
|
|
||||||
for (int z = 0; z < 16; ++z) {
|
|
||||||
BlockVector3 pt = min.add(x, y, z);
|
|
||||||
int index = y * 16 * 16 + z * 16 + x;
|
|
||||||
|
|
||||||
// We have to restore the block if it was outside
|
|
||||||
if (!region.contains(pt)) {
|
|
||||||
editSession.smartSetBlock(pt, history[index]);
|
|
||||||
} else { // Otherwise fool with history
|
|
||||||
editSession.getChangeSet().add(new BlockChange(pt, history[index], editSession.getFullBlock(pt)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
*/
|
|
||||||
return editSession.regenerate(region);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,11 +25,11 @@ import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
|||||||
import com.boydti.fawe.bukkit.FaweBukkit;
|
import com.boydti.fawe.bukkit.FaweBukkit;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItem;
|
import com.sk89q.worldedit.blocks.BaseItem;
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
@ -37,6 +37,7 @@ import com.sk89q.worldedit.registry.state.Property;
|
|||||||
import com.sk89q.worldedit.util.Direction;
|
import com.sk89q.worldedit.util.Direction;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.world.DataFixer;
|
import com.sk89q.worldedit.world.DataFixer;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -236,11 +237,12 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
|
|||||||
* Regenerate a region in the given world, so it appears "as new".
|
* Regenerate a region in the given world, so it appears "as new".
|
||||||
* @param world the world to regen in
|
* @param world the world to regen in
|
||||||
* @param region the region to regen
|
* @param region the region to regen
|
||||||
* @param session the session to use for setting blocks
|
* @param extent the extent to use for setting blocks
|
||||||
|
* @param options the regeneration options
|
||||||
* @return true on success, false on failure
|
* @return true on success, false on failure
|
||||||
*/
|
*/
|
||||||
default boolean regenerate(org.bukkit.World world, Region region, EditSession session) {
|
default boolean regenerate(World world, Region region, Extent extent, RegenOptions options) throws Exception{
|
||||||
return session.regenerate(region);
|
throw new UnsupportedOperationException("This adapter does not support regeneration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
default IChunkGet get(World world, int chunkX, int chunkZ) {
|
default IChunkGet get(World world, int chunkX, int chunkZ) {
|
||||||
|
@ -0,0 +1,545 @@
|
|||||||
|
package com.sk89q.worldedit.bukkit.adapter;
|
||||||
|
|
||||||
|
import com.boydti.fawe.beta.IChunkCache;
|
||||||
|
import com.boydti.fawe.beta.IChunkGet;
|
||||||
|
import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent;
|
||||||
|
import com.boydti.fawe.config.Settings;
|
||||||
|
import com.boydti.fawe.util.MathMan;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.bukkit.generator.BlockPopulator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an abstract regeneration handler.
|
||||||
|
* @param <IChunkAccess> the type of the {@Code IChunkAccess} of the current Minecraft implementation
|
||||||
|
* @param <ProtoChunk> the type of the {@Code ProtoChunk} of the current Minecraft implementation
|
||||||
|
* @param <Chunk> the type of the {@Code Chunk} of the current Minecraft implementation
|
||||||
|
* @param <ChunkStatus> the type of the {@Code ChunkStatusWrapper} wrapping the {@Code ChunkStatus} enum
|
||||||
|
*/
|
||||||
|
public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess, Chunk extends IChunkAccess, ChunkStatus extends Regenerator.ChunkStatusWrapper<IChunkAccess>> {
|
||||||
|
|
||||||
|
public static final Logger logger = LoggerFactory.getLogger(Regenerator.class);
|
||||||
|
|
||||||
|
protected final org.bukkit.World originalBukkitWorld;
|
||||||
|
protected final Region region;
|
||||||
|
protected final Extent target;
|
||||||
|
protected final RegenOptions options;
|
||||||
|
|
||||||
|
//runtime
|
||||||
|
protected final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||||
|
protected boolean generateConcurrent = true;
|
||||||
|
protected long seed;
|
||||||
|
|
||||||
|
private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
|
||||||
|
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
|
||||||
|
private ExecutorService executor;
|
||||||
|
private SingleThreadQueueExtent source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an abstract regeneration handler.
|
||||||
|
* @param originalBukkitWorld the Bukkit world containing all the information on how to regenerate the {code Region}
|
||||||
|
* @param region the selection to regenerate
|
||||||
|
* @param target the target {@code Extent} to paste the regenerated blocks into
|
||||||
|
* @param options the options to used while regenerating and pasting into the target {@code Extent}
|
||||||
|
*/
|
||||||
|
public Regenerator(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||||
|
this.originalBukkitWorld = originalBukkitWorld;
|
||||||
|
this.region = region;
|
||||||
|
this.target = target;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerates the selected {@code Region}.
|
||||||
|
* @return whether or not the regeneration process was successful
|
||||||
|
* @throws Exception when something goes terribly wrong
|
||||||
|
*/
|
||||||
|
public boolean regenerate() throws Exception {
|
||||||
|
if (!prepare()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!initNewWorld()) {
|
||||||
|
cleanup0();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
cleanup0();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!generate()) {
|
||||||
|
cleanup0();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
cleanup0();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
copyToWorld();
|
||||||
|
} catch (Exception e) {
|
||||||
|
cleanup0();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup0();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
protected Chunk getChunkAt(int x, int z) {
|
||||||
|
return chunks.get(MathMan.pairInt(x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean generate() throws Exception {
|
||||||
|
if (generateConcurrent) {
|
||||||
|
//Using concurrent chunk generation
|
||||||
|
executor = Executors.newFixedThreadPool(Settings.IMP.QUEUE.PARALLEL_THREADS);
|
||||||
|
} // else using sequential chunk generation, concurrent not supported
|
||||||
|
|
||||||
|
//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
|
||||||
|
|
||||||
|
//generate chunk coords lists with a certain radius
|
||||||
|
Int2ObjectOpenHashMap<List<Long>> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>();
|
||||||
|
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeigborChunkRadius0).distinct().forEach(radius -> {
|
||||||
|
if (radius == -1) //ignore ChunkStatus.EMPTY
|
||||||
|
return;
|
||||||
|
int border = 16 - radius; //9 = 8 + 1, 8: max border radius used in chunk stages, 1: need 1 extra chunk for chunk features to generate at the border of the region
|
||||||
|
chunkCoordsForRadius.put(radius, getChunkCoordsRegen(region, border));
|
||||||
|
});
|
||||||
|
|
||||||
|
//create chunks
|
||||||
|
for (Long xz : chunkCoordsForRadius.get(0)) {
|
||||||
|
ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz));
|
||||||
|
protoChunks.put(xz, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate lists for RegionLimitedWorldAccess, need to be square with odd length (e.g. 17x17), 17 = 1 middle chunk + 8 border chunks * 2
|
||||||
|
Int2ObjectOpenHashMap<Long2ObjectOpenHashMap<List<IChunkAccess>>> worldlimits = new Int2ObjectOpenHashMap<>();
|
||||||
|
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeigborChunkRadius0).distinct().forEach(radius -> {
|
||||||
|
if (radius == -1) //ignore ChunkStatus.EMPTY
|
||||||
|
return;
|
||||||
|
Long2ObjectOpenHashMap<List<IChunkAccess>> map = new Long2ObjectOpenHashMap<>();
|
||||||
|
for (Long xz : chunkCoordsForRadius.get(radius)) {
|
||||||
|
int x = MathMan.unpairIntX(xz);
|
||||||
|
int z = MathMan.unpairIntY(xz);
|
||||||
|
List<IChunkAccess> l = new ArrayList<>((radius + 1 + radius) * (radius + 1 + radius));
|
||||||
|
for (int zz = z - radius; zz <= z + radius; zz++) { //order is important, first z then x
|
||||||
|
for (int xx = x - radius; xx <= x + radius; xx++) {
|
||||||
|
l.add(protoChunks.get(MathMan.pairInt(xx, zz)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.put(xz, l);
|
||||||
|
}
|
||||||
|
worldlimits.put(radius, map);
|
||||||
|
});
|
||||||
|
|
||||||
|
//run generation tasks exluding FULL chunk status
|
||||||
|
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStati.entrySet()) {
|
||||||
|
ChunkStatus chunkStatus = entry.getKey();
|
||||||
|
int radius = chunkStatus.requiredNeigborChunkRadius0();
|
||||||
|
|
||||||
|
List<Long> coords = chunkCoordsForRadius.get(radius);
|
||||||
|
if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
|
||||||
|
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks = getChunkStatusTaskRows(coords, radius);
|
||||||
|
for (ConcurrentTasks<SequentialTasks<Long>> para : tasks) {
|
||||||
|
List scheduled = new ArrayList<>(tasks.size());
|
||||||
|
for (SequentialTasks<Long> row : para) {
|
||||||
|
scheduled.add((Callable) () -> {
|
||||||
|
for (Long xz : row) {
|
||||||
|
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
List<Future> futures = executor.invokeAll(scheduled);
|
||||||
|
for (Future future : futures) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
|
||||||
|
// every chunk can be processed individually
|
||||||
|
List scheduled = new ArrayList(coords.size());
|
||||||
|
for (long xz : coords) {
|
||||||
|
scheduled.add((Callable) () -> {
|
||||||
|
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
List<Future> futures = executor.invokeAll(scheduled);
|
||||||
|
for (Future future : futures) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else { // Concurrency.NONE or generateConcurrent == false
|
||||||
|
// run sequential
|
||||||
|
for (long xz : coords) {
|
||||||
|
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert to proper chunks
|
||||||
|
for (Long xz : chunkCoordsForRadius.get(0)) {
|
||||||
|
ProtoChunk proto = protoChunks.get(xz);
|
||||||
|
chunks.put(xz, createChunk(proto));
|
||||||
|
}
|
||||||
|
|
||||||
|
//final chunkstatus
|
||||||
|
ChunkStatus FULL = getFullChunkStatus();
|
||||||
|
for (Long xz : chunkCoordsForRadius.get(0)) { //FULL.requiredNeighbourChunkRadius() == 0!
|
||||||
|
Chunk chunk = chunks.get(xz);
|
||||||
|
FULL.processChunkSave(xz, Arrays.asList(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
//populate
|
||||||
|
List<BlockPopulator> populators = getBlockPopulators();
|
||||||
|
for (Long xz : chunkCoordsForRadius.get(0)) {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
source = new SingleThreadQueueExtent();
|
||||||
|
source.init(null, initSourceQueueCache(), null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyToWorld() {
|
||||||
|
//Setting Blocks
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
boolean genbiomes = options.shouldRegenBiomes();
|
||||||
|
for (BlockVector3 vec : region) {
|
||||||
|
target.setBlock(vec, source.getBlock(vec));
|
||||||
|
if (genbiomes) {
|
||||||
|
target.setBiome(vec, source.getBiome(vec));
|
||||||
|
}
|
||||||
|
// realExtent.setSkyLight(vec, extent.getSkyLight(vec));
|
||||||
|
// realExtent.setBlockLight(vec, extent.getBrightness(vec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanup0() {
|
||||||
|
if (executor != null) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
//functions to be implemented by sub class
|
||||||
|
/**
|
||||||
|
* <p>Implement the preparation process in here. DO NOT instanciate any variable here that require the cleanup function. This function is for gathering further information before initializing a new
|
||||||
|
* world.</p>
|
||||||
|
*
|
||||||
|
* <p>Fields required to be initialized: chunkStati, seed</p>
|
||||||
|
* <p>For chunkStati also see {code ChunkStatusWrapper}.</p>
|
||||||
|
*
|
||||||
|
* @return whether or not the preparation process was successful
|
||||||
|
*/
|
||||||
|
protected abstract boolean prepare();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement the creation of the seperate world in here.
|
||||||
|
*
|
||||||
|
* Fields required to be initialized: generateConcurrent
|
||||||
|
*
|
||||||
|
* @return true if everything went fine, otherwise false. When false is returned the Regenerator halts the regeneration process and calls the cleanup function.
|
||||||
|
* @throws java.lang.Exception When the implementation of this method throws and exception the Regenerator halts the regeneration process and calls the cleanup function.
|
||||||
|
*/
|
||||||
|
protected abstract boolean initNewWorld() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement the cleanup of all the mess that is created during the regeneration process (initNewWorld() and generate()).This function must not throw any exceptions.
|
||||||
|
*/
|
||||||
|
protected abstract void cleanup();
|
||||||
|
|
||||||
|
//functions to implement by sub class - regenate related
|
||||||
|
/**
|
||||||
|
* 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<BlockPopulator> 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<IChunkGet>} here. Use will need the {@code getChunkAt} function
|
||||||
|
* @return an initialized {@code IChunkCache<IChunkGet>}
|
||||||
|
*/
|
||||||
|
protected abstract IChunkCache<IChunkGet> initSourceQueueCache();
|
||||||
|
|
||||||
|
//algorithms
|
||||||
|
private List<Long> getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks
|
||||||
|
BlockVector3 oldMin = region.getMinimumPoint();
|
||||||
|
BlockVector3 newMin = BlockVector3.at((oldMin.getX() >> 4 << 4) - border * 16, oldMin.getY(), (oldMin.getZ() >> 4 << 4) - border * 16);
|
||||||
|
BlockVector3 oldMax = region.getMaximumPoint();
|
||||||
|
BlockVector3 newMax = BlockVector3.at((oldMax.getX() >> 4 << 4) + (border + 1) * 16 - 1, oldMax.getY(), (oldMax.getZ() >> 4 << 4) + (border + 1) * 16 - 1);
|
||||||
|
Region adjustedRegion = new CuboidRegion(newMin, newMax);
|
||||||
|
return adjustedRegion.getChunks().stream()
|
||||||
|
.map(c -> BlockVector2.at(c.getX(), c.getZ()))
|
||||||
|
.sorted(Comparator.<BlockVector2>comparingInt(c -> c.getZ()).thenComparingInt(c -> c.getX())) //needed for RegionLimitedWorldAccess
|
||||||
|
.map(c -> MathMan.pairInt(c.getX(), c.getZ()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 conccurently (ChunkStatus.requiredNeighborRadius)
|
||||||
|
* @return a list of chunkcoords rows that may be executed concurrently
|
||||||
|
*/
|
||||||
|
private SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> getChunkStatusTaskRows(List<Long> allcoords, int requiredNeighborChunkRadius) {
|
||||||
|
int requiredneighbors = Math.max(0, requiredNeighborChunkRadius);
|
||||||
|
|
||||||
|
int minx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(0));
|
||||||
|
int maxx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(allcoords.size() - 1));
|
||||||
|
int minz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(0));
|
||||||
|
int maxz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(allcoords.size() - 1));
|
||||||
|
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks;
|
||||||
|
if (maxz - minz > maxx - minx) {
|
||||||
|
int numlists = Math.min(requiredneighbors * 2 + 1, maxx - minx + 1);
|
||||||
|
|
||||||
|
Int2ObjectOpenHashMap<SequentialTasks<Long>> byx = new Int2ObjectOpenHashMap();
|
||||||
|
int expectedListLength = (allcoords.size() + 1) / (maxx - minx);
|
||||||
|
|
||||||
|
//init lists
|
||||||
|
for (int i = minx; i <= maxx; i++) {
|
||||||
|
byx.put(i, new SequentialTasks(expectedListLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort into lists by x coord
|
||||||
|
for (Long xz : allcoords) {
|
||||||
|
byx.get(MathMan.unpairIntX(xz)).add(xz);
|
||||||
|
}
|
||||||
|
|
||||||
|
//create parallel tasks
|
||||||
|
tasks = new SequentialTasks(numlists);
|
||||||
|
for (int offset = 0; offset < numlists; offset++) {
|
||||||
|
ConcurrentTasks<SequentialTasks<Long>> 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<SequentialTasks<Long>> byz = new Int2ObjectOpenHashMap();
|
||||||
|
int expectedListLength = (allcoords.size() + 1) / (maxz - minz);
|
||||||
|
|
||||||
|
//init lists
|
||||||
|
for (int i = minz; i <= maxz; i++) {
|
||||||
|
byz.put(i, new SequentialTasks(expectedListLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort into lists by x coord
|
||||||
|
for (Long xz : allcoords) {
|
||||||
|
byz.get(MathMan.unpairIntY(xz)).add(xz);
|
||||||
|
}
|
||||||
|
|
||||||
|
//create parallel tasks
|
||||||
|
tasks = new SequentialTasks(numlists);
|
||||||
|
for (int offset = 0; offset < numlists; offset++) {
|
||||||
|
ConcurrentTasks<SequentialTasks<Long>> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//classes
|
||||||
|
/**
|
||||||
|
* This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step.
|
||||||
|
* @param <IChunkAccess> the IChunkAccess class of the current Minecraft implementation
|
||||||
|
*/
|
||||||
|
public static abstract class ChunkStatusWrapper<IChunkAccess> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the required neighbor chunk radius the wrapped {@code ChunkStatus} requires.
|
||||||
|
*
|
||||||
|
* @return the radius of required neighbor chunks
|
||||||
|
*/
|
||||||
|
public abstract int requiredNeigborChunkRadius();
|
||||||
|
|
||||||
|
int requiredNeigborChunkRadius0() {
|
||||||
|
return Math.max(0, requiredNeigborChunkRadius());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 xz represents the chunk coordinates of the chunk to process as denoted by {@code MathMan}
|
||||||
|
* @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 void processChunk(Long xz, List<IChunkAccess> accessibleChunks);
|
||||||
|
|
||||||
|
void processChunkSave(Long xz, List<IChunkAccess> accessibleChunks) {
|
||||||
|
try {
|
||||||
|
processChunk(xz, accessibleChunks);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Concurrency {
|
||||||
|
FULL,
|
||||||
|
RADIUS,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SequentialTasks<T> extends Tasks<T> {
|
||||||
|
|
||||||
|
public SequentialTasks(int expectedsize) {
|
||||||
|
super(expectedsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ConcurrentTasks<T> extends Tasks<T> {
|
||||||
|
|
||||||
|
public ConcurrentTasks(int expectedsize) {
|
||||||
|
super(expectedsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Tasks<T> implements Iterable<T> {
|
||||||
|
|
||||||
|
private final List<T> tasks;
|
||||||
|
|
||||||
|
public Tasks(int expectedsize) {
|
||||||
|
tasks = new ArrayList(expectedsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(T task) {
|
||||||
|
tasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> list() {
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return tasks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return tasks.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return tasks.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,36 +22,33 @@ package com.sk89q.worldedit.bukkit.adapter.impl;
|
|||||||
import com.boydti.fawe.Fawe;
|
import com.boydti.fawe.Fawe;
|
||||||
import com.boydti.fawe.FaweCache;
|
import com.boydti.fawe.FaweCache;
|
||||||
import com.boydti.fawe.beta.IChunkGet;
|
import com.boydti.fawe.beta.IChunkGet;
|
||||||
import com.boydti.fawe.beta.IQueueChunk;
|
|
||||||
import com.boydti.fawe.beta.IQueueExtent;
|
|
||||||
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
|
||||||
import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent;
|
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BlockMaterial_1_15_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BlockMaterial_1_15_2;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitAdapter_1_15_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitAdapter_1_15_2;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_15_2.FAWEWorldNativeAccess_1_15_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.FAWEWorldNativeAccess_1_15_2;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_15_2.MapChunkUtil_1_15_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.MapChunkUtil_1_15_2;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.base.Preconditions;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.StringTag;
|
import com.sk89q.jnbt.StringTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||||
|
import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_15_R2;
|
||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.registry.state.Property;
|
import com.sk89q.worldedit.registry.state.Property;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -66,7 +63,6 @@ import net.minecraft.server.v1_15_R1.Block;
|
|||||||
import net.minecraft.server.v1_15_R1.BlockPosition;
|
import net.minecraft.server.v1_15_R1.BlockPosition;
|
||||||
import net.minecraft.server.v1_15_R1.Chunk;
|
import net.minecraft.server.v1_15_R1.Chunk;
|
||||||
import net.minecraft.server.v1_15_R1.ChunkCoordIntPair;
|
import net.minecraft.server.v1_15_R1.ChunkCoordIntPair;
|
||||||
import net.minecraft.server.v1_15_R1.ChunkProviderServer;
|
|
||||||
import net.minecraft.server.v1_15_R1.ChunkSection;
|
import net.minecraft.server.v1_15_R1.ChunkSection;
|
||||||
import net.minecraft.server.v1_15_R1.Entity;
|
import net.minecraft.server.v1_15_R1.Entity;
|
||||||
import net.minecraft.server.v1_15_R1.EntityPlayer;
|
import net.minecraft.server.v1_15_R1.EntityPlayer;
|
||||||
@ -75,7 +71,6 @@ import net.minecraft.server.v1_15_R1.IBlockData;
|
|||||||
import net.minecraft.server.v1_15_R1.IRegistry;
|
import net.minecraft.server.v1_15_R1.IRegistry;
|
||||||
import net.minecraft.server.v1_15_R1.ItemStack;
|
import net.minecraft.server.v1_15_R1.ItemStack;
|
||||||
import net.minecraft.server.v1_15_R1.MinecraftKey;
|
import net.minecraft.server.v1_15_R1.MinecraftKey;
|
||||||
import net.minecraft.server.v1_15_R1.MinecraftServer;
|
|
||||||
import net.minecraft.server.v1_15_R1.NBTBase;
|
import net.minecraft.server.v1_15_R1.NBTBase;
|
||||||
import net.minecraft.server.v1_15_R1.NBTTagCompound;
|
import net.minecraft.server.v1_15_R1.NBTTagCompound;
|
||||||
import net.minecraft.server.v1_15_R1.NBTTagInt;
|
import net.minecraft.server.v1_15_R1.NBTTagInt;
|
||||||
@ -83,12 +78,9 @@ import net.minecraft.server.v1_15_R1.PacketPlayOutMapChunk;
|
|||||||
import net.minecraft.server.v1_15_R1.PlayerChunk;
|
import net.minecraft.server.v1_15_R1.PlayerChunk;
|
||||||
import net.minecraft.server.v1_15_R1.TileEntity;
|
import net.minecraft.server.v1_15_R1.TileEntity;
|
||||||
import net.minecraft.server.v1_15_R1.World;
|
import net.minecraft.server.v1_15_R1.World;
|
||||||
import net.minecraft.server.v1_15_R1.WorldData;
|
|
||||||
import net.minecraft.server.v1_15_R1.WorldNBTStorage;
|
|
||||||
import net.minecraft.server.v1_15_R1.WorldServer;
|
import net.minecraft.server.v1_15_R1.WorldServer;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World.Environment;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
|
||||||
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
|
||||||
@ -98,17 +90,11 @@ import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
|
|||||||
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
|
||||||
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -119,7 +105,6 @@ import static org.slf4j.LoggerFactory.getLogger;
|
|||||||
public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<NBTBase> {
|
public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<NBTBase> {
|
||||||
private final Spigot_v1_15_R2 parent;
|
private final Spigot_v1_15_R2 parent;
|
||||||
private char[] ibdToStateOrdinal;
|
private char[] ibdToStateOrdinal;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Code that may break between versions of Minecraft
|
// Code that may break between versions of Minecraft
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -157,8 +142,6 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
|
|||||||
public BlockMaterial getMaterial(BlockState state) {
|
public BlockMaterial getMaterial(BlockState state) {
|
||||||
IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||||
return new BlockMaterial_1_15_2(bs.getBlock(), bs);
|
return new BlockMaterial_1_15_2(bs.getBlock(), bs);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block getBlock(BlockType blockType) {
|
public Block getBlock(BlockType blockType) {
|
||||||
@ -168,7 +151,7 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public BaseBlock getBlock(Location location) {
|
public BaseBlock getBlock(Location location) {
|
||||||
checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
@ -272,7 +255,7 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||||
checkNotNull(entity);
|
Preconditions.checkNotNull(entity);
|
||||||
|
|
||||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||||
Entity mcEntity = craftEntity.getHandle();
|
Entity mcEntity = craftEntity.getHandle();
|
||||||
@ -417,83 +400,9 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
|
|||||||
}
|
}
|
||||||
return parent.fromNative(foreign);
|
return parent.fromNative(foreign);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) {
|
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||||
WorldServer originalWorld = ((CraftWorld) world).getHandle();
|
return new Regen_v1_15_R2(bukkitWorld, region, target, options).regenerate();
|
||||||
ChunkProviderServer provider = originalWorld.getChunkProvider();
|
|
||||||
if (!(provider instanceof ChunkProviderServer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
File saveFolder = Files.createTempDir();
|
|
||||||
// register this just in case something goes wrong
|
|
||||||
// normally it should be deleted at the end of this method
|
|
||||||
saveFolder.deleteOnExit();
|
|
||||||
try {
|
|
||||||
MinecraftServer server = originalWorld.getServer().getServer();
|
|
||||||
WorldNBTStorage originalDataManager = originalWorld.getDataManager();
|
|
||||||
WorldNBTStorage saveHandler = new WorldNBTStorage(saveFolder, originalDataManager.getDirectory().getName(), server, originalDataManager.getDataFixer());
|
|
||||||
WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null),
|
|
||||||
server.dataConverterManager, getDataVersion(), null);
|
|
||||||
newWorldData.setName(UUID.randomUUID().toString());
|
|
||||||
|
|
||||||
ChunkGenerator gen = world.getGenerator();
|
|
||||||
Environment env = world.getEnvironment();
|
|
||||||
try (WorldServer freshWorld = new WorldServer(server,
|
|
||||||
server.executorService, saveHandler,
|
|
||||||
newWorldData,
|
|
||||||
originalWorld.worldProvider.getDimensionManager(),
|
|
||||||
originalWorld.getMethodProfiler(),
|
|
||||||
server.worldLoadListenerFactory.create(11),
|
|
||||||
env,
|
|
||||||
gen) {
|
|
||||||
@Override
|
|
||||||
public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) {
|
|
||||||
//Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
|
|
||||||
// Pre-gen all the chunks
|
|
||||||
// We need to also pull one more chunk in every direction
|
|
||||||
Fawe.get().getQueueHandler().startSet(true);
|
|
||||||
try {
|
|
||||||
IQueueExtent<IQueueChunk> extent = new SingleThreadQueueExtent();
|
|
||||||
extent.init(null, (x, z) -> new BukkitGetBlocks_1_15_2(freshWorld, x, z) {
|
|
||||||
@Override
|
|
||||||
public Chunk ensureLoaded(World nmsWorld, int chunkX, int chunkZ) {
|
|
||||||
Chunk cached = nmsWorld.getChunkIfLoaded(chunkX, chunkZ);
|
|
||||||
if (cached != null) {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
Future<Chunk> future = Fawe.get().getQueueHandler().sync((Supplier<Chunk>) () -> freshWorld.getChunkAt(chunkX, chunkZ));
|
|
||||||
while (!future.isDone()) {
|
|
||||||
// this feels so dirty
|
|
||||||
freshWorld.getChunkProvider().runTasks();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return future.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
for (BlockVector3 vec : region) {
|
|
||||||
editSession.setBlock(vec, extent.getFullBlock(vec));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
Fawe.get().getQueueHandler().endSet(true);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} catch (MaxChangedBlocksException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
saveFolder.delete();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,23 +28,26 @@ import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1;
|
|||||||
import com.boydti.fawe.bukkit.adapter.mc1_16_1.FAWEWorldNativeAccess_1_16;
|
import com.boydti.fawe.bukkit.adapter.mc1_16_1.FAWEWorldNativeAccess_1_16;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_16_1.MapChunkUtil_1_16_1;
|
import com.boydti.fawe.bukkit.adapter.mc1_16_1.MapChunkUtil_1_16_1;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1;
|
import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.StringTag;
|
import com.sk89q.jnbt.StringTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||||
|
import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_16_R1;
|
||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.registry.state.Property;
|
import com.sk89q.worldedit.registry.state.Property;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -101,7 +104,6 @@ import static org.slf4j.LoggerFactory.getLogger;
|
|||||||
public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<NBTBase> {
|
public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<NBTBase> {
|
||||||
private final Spigot_v1_16_R1 parent;
|
private final Spigot_v1_16_R1 parent;
|
||||||
private char[] ibdToStateOrdinal;
|
private char[] ibdToStateOrdinal;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Code that may break between versions of Minecraft
|
// Code that may break between versions of Minecraft
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -139,8 +141,6 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I
|
|||||||
public BlockMaterial getMaterial(BlockState state) {
|
public BlockMaterial getMaterial(BlockState state) {
|
||||||
IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||||
return new BlockMaterial_1_16_1(bs.getBlock(), bs);
|
return new BlockMaterial_1_16_1(bs.getBlock(), bs);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block getBlock(BlockType blockType) {
|
public Block getBlock(BlockType blockType) {
|
||||||
@ -150,7 +150,7 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public BaseBlock getBlock(Location location) {
|
public BaseBlock getBlock(Location location) {
|
||||||
checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
@ -254,7 +254,7 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||||
checkNotNull(entity);
|
Preconditions.checkNotNull(entity);
|
||||||
|
|
||||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||||
Entity mcEntity = craftEntity.getHandle();
|
Entity mcEntity = craftEntity.getHandle();
|
||||||
@ -401,81 +401,8 @@ public final class FAWE_Spigot_v1_16_R1 extends CachedBukkitAdapter implements I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) {
|
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||||
// WorldServer originalWorld = ((CraftWorld) world).getHandle();
|
return new Regen_v1_16_R1(bukkitWorld, region, target, options).regenerate();
|
||||||
// ChunkProviderServer provider = originalWorld.getChunkProvider();
|
|
||||||
// if (!(provider instanceof ChunkProviderServer)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// File saveFolder = Files.createTempDir();
|
|
||||||
// // register this just in case something goes wrong
|
|
||||||
// // normally it should be deleted at the end of this method
|
|
||||||
// saveFolder.deleteOnExit();
|
|
||||||
// try {
|
|
||||||
// MinecraftServer server = originalWorld.getServer().getServer();
|
|
||||||
// Convertable.ConversionSession originalDataManager = server.convertable;
|
|
||||||
//// Convertable.ConversionSession saveHandler = new Convertable.ConversionSession(world.getName(), world.);
|
|
||||||
// WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null),
|
|
||||||
// server.dataConverterManager, getDataVersion(), null);
|
|
||||||
// newWorldData.setName(UUID.randomUUID().toString());
|
|
||||||
//
|
|
||||||
// ChunkGenerator gen = world.getGenerator();
|
|
||||||
// Environment env = world.getEnvironment();
|
|
||||||
// try (WorldServer freshWorld = new WorldServer(server,
|
|
||||||
// server.executorService, originalDataManager,
|
|
||||||
// newWorldData,
|
|
||||||
// originalWorld.worldProvider.getDimensionManager(),
|
|
||||||
// originalWorld.getMethodProfiler(),
|
|
||||||
// server.worldLoadListenerFactory.create(11),
|
|
||||||
// env,
|
|
||||||
// gen) {
|
|
||||||
// @Override
|
|
||||||
// public boolean addEntityChunk(Entity entity) {
|
|
||||||
// //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }) {
|
|
||||||
//
|
|
||||||
// // Pre-gen all the chunks
|
|
||||||
// // We need to also pull one more chunk in every direction
|
|
||||||
// Fawe.get().getQueueHandler().startSet(true);
|
|
||||||
// try {
|
|
||||||
// IQueueExtent<IQueueChunk> extent = new SingleThreadQueueExtent();
|
|
||||||
// extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_1(freshWorld, x, z) {
|
|
||||||
// @Override
|
|
||||||
// public Chunk ensureLoaded(World nmsWorld, int X, int Z) {
|
|
||||||
// Chunk cached = nmsWorld.getChunkIfLoaded(X, Z);
|
|
||||||
// if (cached != null) return cached;
|
|
||||||
// Future<Chunk> future = Fawe.get().getQueueHandler().sync((Supplier<Chunk>) () -> freshWorld.getChunkAt(X, Z));
|
|
||||||
// while (!future.isDone()) {
|
|
||||||
// // this feels so dirty
|
|
||||||
// freshWorld.getChunkProvider().runTasks();
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// return future.get();
|
|
||||||
// } catch (InterruptedException | ExecutionException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }, null);
|
|
||||||
// for (BlockVector3 vec : region) {
|
|
||||||
// editSession.setBlock(vec, extent.getFullBlock(vec));
|
|
||||||
// }
|
|
||||||
// } finally {
|
|
||||||
// Fawe.get().getQueueHandler().endSet(true);
|
|
||||||
// }
|
|
||||||
// } catch (IOException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
// } catch (MaxChangedBlocksException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// } finally {
|
|
||||||
// saveFolder.delete();
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
|
|
||||||
return false; //TODO: rework or remove for 1.16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,23 +28,26 @@ import com.boydti.fawe.bukkit.adapter.mc1_16_2.BukkitGetBlocks_1_16_2;
|
|||||||
import com.boydti.fawe.bukkit.adapter.mc1_16_2.FAWEWorldNativeAccess_1_16;
|
import com.boydti.fawe.bukkit.adapter.mc1_16_2.FAWEWorldNativeAccess_1_16;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_16_2.MapChunkUtil_1_16_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_16_2.MapChunkUtil_1_16_2;
|
||||||
import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2;
|
import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.StringTag;
|
import com.sk89q.jnbt.StringTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||||
|
import com.sk89q.worldedit.bukkit.adapter.impl.regen.Regen_v1_16_R2;
|
||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
import com.sk89q.worldedit.entity.LazyBaseEntity;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.registry.state.Property;
|
import com.sk89q.worldedit.registry.state.Property;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -87,7 +90,6 @@ import org.bukkit.craftbukkit.v1_16_R2.entity.CraftEntity;
|
|||||||
import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
|
||||||
import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack;
|
import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
@ -149,7 +151,7 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public BaseBlock getBlock(Location location) {
|
public BaseBlock getBlock(Location location) {
|
||||||
checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
@ -253,7 +255,7 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||||
checkNotNull(entity);
|
Preconditions.checkNotNull(entity);
|
||||||
|
|
||||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||||
Entity mcEntity = craftEntity.getHandle();
|
Entity mcEntity = craftEntity.getHandle();
|
||||||
@ -400,81 +402,8 @@ public final class FAWE_Spigot_v1_16_R2 extends CachedBukkitAdapter implements I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) {
|
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||||
// WorldServer originalWorld = ((CraftWorld) world).getHandle();
|
return new Regen_v1_16_R2(bukkitWorld, region, target, options).regenerate();
|
||||||
// ChunkProviderServer provider = originalWorld.getChunkProvider();
|
|
||||||
// if (!(provider instanceof ChunkProviderServer)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// File saveFolder = Files.createTempDir();
|
|
||||||
// // register this just in case something goes wrong
|
|
||||||
// // normally it should be deleted at the end of this method
|
|
||||||
// saveFolder.deleteOnExit();
|
|
||||||
// try {
|
|
||||||
// MinecraftServer server = originalWorld.getServer().getServer();
|
|
||||||
// Convertable.ConversionSession originalDataManager = server.convertable;
|
|
||||||
//// Convertable.ConversionSession saveHandler = new Convertable.ConversionSession(world.getName(), world.);
|
|
||||||
// WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null),
|
|
||||||
// server.dataConverterManager, getDataVersion(), null);
|
|
||||||
// newWorldData.setName(UUID.randomUUID().toString());
|
|
||||||
//
|
|
||||||
// ChunkGenerator gen = world.getGenerator();
|
|
||||||
// Environment env = world.getEnvironment();
|
|
||||||
// try (WorldServer freshWorld = new WorldServer(server,
|
|
||||||
// server.executorService, originalDataManager,
|
|
||||||
// newWorldData,
|
|
||||||
// originalWorld.worldProvider.getDimensionManager(),
|
|
||||||
// originalWorld.getMethodProfiler(),
|
|
||||||
// server.worldLoadListenerFactory.create(11),
|
|
||||||
// env,
|
|
||||||
// gen) {
|
|
||||||
// @Override
|
|
||||||
// public boolean addEntityChunk(Entity entity) {
|
|
||||||
// //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }) {
|
|
||||||
//
|
|
||||||
// // Pre-gen all the chunks
|
|
||||||
// // We need to also pull one more chunk in every direction
|
|
||||||
// Fawe.get().getQueueHandler().startSet(true);
|
|
||||||
// try {
|
|
||||||
// IQueueExtent<IQueueChunk> extent = new SingleThreadQueueExtent();
|
|
||||||
// extent.init(null, (x, z) -> new BukkitGetBlocks_1_16_2(freshWorld, x, z) {
|
|
||||||
// @Override
|
|
||||||
// public Chunk ensureLoaded(World nmsWorld, int X, int Z) {
|
|
||||||
// Chunk cached = nmsWorld.getChunkIfLoaded(X, Z);
|
|
||||||
// if (cached != null) return cached;
|
|
||||||
// Future<Chunk> future = Fawe.get().getQueueHandler().sync((Supplier<Chunk>) () -> freshWorld.getChunkAt(X, Z));
|
|
||||||
// while (!future.isDone()) {
|
|
||||||
// // this feels so dirty
|
|
||||||
// freshWorld.getChunkProvider().runTasks();
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// return future.get();
|
|
||||||
// } catch (InterruptedException | ExecutionException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }, null);
|
|
||||||
// for (BlockVector3 vec : region) {
|
|
||||||
// editSession.setBlock(vec, extent.getFullBlock(vec));
|
|
||||||
// }
|
|
||||||
// } finally {
|
|
||||||
// Fawe.get().getQueueHandler().endSet(true);
|
|
||||||
// }
|
|
||||||
// } catch (IOException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
// } catch (MaxChangedBlocksException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// } finally {
|
|
||||||
// saveFolder.delete();
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
|
|
||||||
return false; //TODO: rework or remove for 1.16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,478 @@
|
|||||||
|
package com.sk89q.worldedit.bukkit.adapter.impl.regen;
|
||||||
|
|
||||||
|
import com.boydti.fawe.Fawe;
|
||||||
|
import com.boydti.fawe.beta.IChunkCache;
|
||||||
|
import com.boydti.fawe.beta.IChunkGet;
|
||||||
|
import com.boydti.fawe.bukkit.adapter.mc1_15_2.BukkitGetBlocks_1_15_2;
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import com.sk89q.worldedit.bukkit.adapter.Regenerator;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.LongFunction;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import net.minecraft.server.v1_15_R1.Area;
|
||||||
|
import net.minecraft.server.v1_15_R1.AreaContextTransformed;
|
||||||
|
import net.minecraft.server.v1_15_R1.AreaFactory;
|
||||||
|
import net.minecraft.server.v1_15_R1.AreaTransformer8;
|
||||||
|
import net.minecraft.server.v1_15_R1.BiomeBase;
|
||||||
|
import net.minecraft.server.v1_15_R1.BiomeLayoutOverworldConfiguration;
|
||||||
|
import net.minecraft.server.v1_15_R1.Biomes;
|
||||||
|
import net.minecraft.server.v1_15_R1.Chunk;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkConverter;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkCoordIntPair;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkGenerator;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkProviderFlat;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkProviderGenerate;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkProviderHell;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkProviderServer;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkProviderTheEnd;
|
||||||
|
import net.minecraft.server.v1_15_R1.ChunkStatus;
|
||||||
|
import net.minecraft.server.v1_15_R1.DefinedStructureManager;
|
||||||
|
import net.minecraft.server.v1_15_R1.GenLayer;
|
||||||
|
import net.minecraft.server.v1_15_R1.GenLayers;
|
||||||
|
import net.minecraft.server.v1_15_R1.GeneratorSettingsEnd;
|
||||||
|
import net.minecraft.server.v1_15_R1.GeneratorSettingsFlat;
|
||||||
|
import net.minecraft.server.v1_15_R1.GeneratorSettingsNether;
|
||||||
|
import net.minecraft.server.v1_15_R1.GeneratorSettingsOverworld;
|
||||||
|
import net.minecraft.server.v1_15_R1.IChunkAccess;
|
||||||
|
import net.minecraft.server.v1_15_R1.IRegistry;
|
||||||
|
import net.minecraft.server.v1_15_R1.LightEngineThreaded;
|
||||||
|
import net.minecraft.server.v1_15_R1.LinearCongruentialGenerator;
|
||||||
|
import net.minecraft.server.v1_15_R1.MinecraftServer;
|
||||||
|
import net.minecraft.server.v1_15_R1.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_15_R1.NoiseGeneratorPerlin;
|
||||||
|
import net.minecraft.server.v1_15_R1.ProtoChunk;
|
||||||
|
import net.minecraft.server.v1_15_R1.World;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldChunkManager;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldChunkManagerOverworld;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldData;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldLoadListener;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldNBTStorage;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldServer;
|
||||||
|
import net.minecraft.server.v1_15_R1.WorldType;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_15_R1.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.craftbukkit.v1_15_R1.util.CraftMagicNumbers;
|
||||||
|
import org.bukkit.generator.BlockPopulator;
|
||||||
|
|
||||||
|
public class Regen_v1_15_R2 extends Regenerator<IChunkAccess, ProtoChunk, Chunk, Regen_v1_15_R2.ChunkStatusWrap> {
|
||||||
|
|
||||||
|
private static final Field serverWorldsField;
|
||||||
|
private static final Field worldPaperConfigField;
|
||||||
|
private static final Field flatBedrockField;
|
||||||
|
private static final Field delegateField;
|
||||||
|
private static final Field chunkProviderField;
|
||||||
|
|
||||||
|
//list of chunk stati in correct order without FULL
|
||||||
|
private static final Map<ChunkStatus, Regenerator.Concurrency> chunkStati = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing
|
||||||
|
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||||
|
chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk
|
||||||
|
chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8
|
||||||
|
chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||||
|
chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||||
|
chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||||
|
chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk
|
||||||
|
chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||||
|
serverWorldsField.setAccessible(true);
|
||||||
|
|
||||||
|
Field tmpPaperConfigField = null;
|
||||||
|
Field tmpFlatBedrockField = null;
|
||||||
|
try { //only present on paper
|
||||||
|
tmpPaperConfigField = World.class.getDeclaredField("paperConfig");
|
||||||
|
tmpPaperConfigField.setAccessible(true);
|
||||||
|
|
||||||
|
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||||
|
tmpFlatBedrockField.setAccessible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
tmpPaperConfigField = null;
|
||||||
|
tmpFlatBedrockField = null;
|
||||||
|
}
|
||||||
|
worldPaperConfigField = tmpPaperConfigField;
|
||||||
|
flatBedrockField = tmpFlatBedrockField;
|
||||||
|
|
||||||
|
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||||
|
delegateField.setAccessible(true);
|
||||||
|
|
||||||
|
chunkProviderField = World.class.getDeclaredField("chunkProvider");
|
||||||
|
chunkProviderField.setAccessible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//runtime
|
||||||
|
private WorldServer originalNMSWorld;
|
||||||
|
private ChunkProviderServer originalChunkProvider;
|
||||||
|
private WorldServer freshNMSWorld;
|
||||||
|
private ChunkProviderServer freshChunkProvider;
|
||||||
|
private DefinedStructureManager structureManager;
|
||||||
|
private LightEngineThreaded lightEngine;
|
||||||
|
private ChunkGenerator generator;
|
||||||
|
|
||||||
|
private Path tempDir;
|
||||||
|
|
||||||
|
private boolean generateFlatBedrock = false;
|
||||||
|
|
||||||
|
public Regen_v1_15_R2(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||||
|
super(originalBukkitWorld, region, target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean prepare() {
|
||||||
|
this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||||
|
originalChunkProvider = originalNMSWorld.getChunkProvider();
|
||||||
|
if (!(originalChunkProvider instanceof ChunkProviderServer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//flat bedrock? (only on paper)
|
||||||
|
try {
|
||||||
|
generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
seed = options.getSeed().orElse(originalNMSWorld.getSeed());
|
||||||
|
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean initNewWorld() throws Exception {
|
||||||
|
//world folder
|
||||||
|
tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen");
|
||||||
|
|
||||||
|
//prepare for world init (see upstream implementation for reference)
|
||||||
|
org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment();
|
||||||
|
org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator();
|
||||||
|
|
||||||
|
MinecraftServer server = originalNMSWorld.getServer().getServer();
|
||||||
|
WorldData newWorldData = new WorldData(originalNMSWorld.worldData.a((NBTTagCompound) null), server.dataConverterManager, CraftMagicNumbers.INSTANCE.getDataVersion(), (NBTTagCompound) null);
|
||||||
|
newWorldData.setName("worldeditregentempworld");
|
||||||
|
WorldNBTStorage saveHandler = new WorldNBTStorage(new File(tempDir.toUri()), originalNMSWorld.getDataManager().getDirectory().getName(), server, server.dataConverterManager);
|
||||||
|
|
||||||
|
//init world
|
||||||
|
freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier<WorldServer>) () -> new WorldServer(server, server.executorService, saveHandler, newWorldData, originalNMSWorld.worldProvider.getDimensionManager(), originalNMSWorld.getMethodProfiler(), new RegenNoOpWorldLoadListener(), env, gen) {
|
||||||
|
@Override
|
||||||
|
public void doTick(BooleanSupplier booleansupplier) { //no ticking
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
freshNMSWorld.savingDisabled = true;
|
||||||
|
removeWorldFromWorldsMap();
|
||||||
|
newWorldData.checkName(originalNMSWorld.getWorldData().getName()); //rename to original world name
|
||||||
|
|
||||||
|
try { //flat bedrock (paper only)
|
||||||
|
Object paperconf = worldPaperConfigField.get(freshNMSWorld);
|
||||||
|
flatBedrockField.setBoolean(paperconf, generateFlatBedrock);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DefinedStructureManager tmpStructureManager = saveHandler.f();
|
||||||
|
freshChunkProvider = new ChunkProviderServer(freshNMSWorld, saveHandler.getDirectory(), server.aC(), tmpStructureManager, server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, new RegenNoOpWorldLoadListener(), () -> freshNMSWorld.getWorldPersistentData()) {
|
||||||
|
// redirect to our protoChunks list
|
||||||
|
@Override
|
||||||
|
public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) {
|
||||||
|
return getProtoChunkAt(x, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chunkProviderField.set(freshNMSWorld, freshChunkProvider);
|
||||||
|
|
||||||
|
//generator
|
||||||
|
if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) {
|
||||||
|
GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) originalChunkProvider.getChunkGenerator().getSettings();
|
||||||
|
generator = new ChunkProviderFlat(freshNMSWorld, originalChunkProvider.getChunkGenerator().getWorldChunkManager(), generatorSettingFlat);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderGenerate) { //overworld
|
||||||
|
GeneratorSettingsOverworld settings = (GeneratorSettingsOverworld) originalChunkProvider.getChunkGenerator().getSettings();
|
||||||
|
WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager();
|
||||||
|
if (chunkManager instanceof WorldChunkManagerOverworld) { //should always be true
|
||||||
|
chunkManager = fastOverWorldChunkManager(chunkManager);
|
||||||
|
}
|
||||||
|
generator = new ChunkProviderGenerate(freshNMSWorld, chunkManager, settings);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderHell) { //nether
|
||||||
|
GeneratorSettingsNether settings = (GeneratorSettingsNether) originalChunkProvider.getChunkGenerator().getSettings();
|
||||||
|
generator = new ChunkProviderHell(freshNMSWorld, originalChunkProvider.getChunkGenerator().getWorldChunkManager(), settings);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderTheEnd) { //end
|
||||||
|
GeneratorSettingsEnd settings = (GeneratorSettingsEnd) originalChunkProvider.getChunkGenerator().getSettings();
|
||||||
|
generator = new ChunkProviderTheEnd(freshNMSWorld, originalChunkProvider.getChunkGenerator().getWorldChunkManager(), settings);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) {
|
||||||
|
ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
generator = delegate;
|
||||||
|
} else {
|
||||||
|
System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (originalNMSWorld.generator != null) {
|
||||||
|
// wrap custom world generator
|
||||||
|
generator = new CustomChunkGenerator(freshNMSWorld, originalNMSWorld.generator);
|
||||||
|
generateConcurrent = originalNMSWorld.generator.isParallelCapable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//lets start then
|
||||||
|
structureManager = tmpStructureManager;
|
||||||
|
lightEngine = freshChunkProvider.getLightEngine();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cleanup() {
|
||||||
|
//shutdown chunk provider
|
||||||
|
try {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
try {
|
||||||
|
freshChunkProvider.close(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove world from server
|
||||||
|
try {
|
||||||
|
removeWorldFromWorldsMap();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//delete directory
|
||||||
|
try {
|
||||||
|
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||||
|
return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) {
|
||||||
|
public boolean generateFlatBedrock() {
|
||||||
|
return generateFlatBedrock;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Chunk createChunk(ProtoChunk protoChunk) {
|
||||||
|
return new Chunk(freshNMSWorld, protoChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChunkStatusWrap getFullChunkStatus() {
|
||||||
|
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BlockPopulator> getBlockPopulators() {
|
||||||
|
return originalNMSWorld.getWorld().getPopulators();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void populate(Chunk chunk, Random random, BlockPopulator pop) {
|
||||||
|
pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||||
|
return (chunkX, chunkZ) -> new BukkitGetBlocks_1_15_2(freshNMSWorld, chunkX, chunkZ) {
|
||||||
|
@Override
|
||||||
|
public Chunk ensureLoaded(World nmsWorld, int x, int z) {
|
||||||
|
return getChunkAt(x, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper<IChunkAccess> {
|
||||||
|
|
||||||
|
private final ChunkStatus chunkStatus;
|
||||||
|
|
||||||
|
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||||
|
this.chunkStatus = chunkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int requiredNeigborChunkRadius() {
|
||||||
|
return chunkStatus.f();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return chunkStatus.d();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processChunk(Long xz, List<IChunkAccess> accessibleChunks) {
|
||||||
|
chunkStatus.a(freshNMSWorld,
|
||||||
|
generator,
|
||||||
|
structureManager,
|
||||||
|
lightEngine,
|
||||||
|
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||||
|
accessibleChunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//util
|
||||||
|
private void removeWorldFromWorldsMap() {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
try {
|
||||||
|
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||||
|
map.remove("worldeditregentempworld");
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception {
|
||||||
|
Field genLayerField = WorldChunkManagerOverworld.class.getDeclaredField("d");
|
||||||
|
genLayerField.setAccessible(true);
|
||||||
|
Field areaLazyField = GenLayer.class.getDeclaredField("b");
|
||||||
|
areaLazyField.setAccessible(true);
|
||||||
|
Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", WorldType.class, GeneratorSettingsOverworld.class, LongFunction.class);
|
||||||
|
initAreaFactoryMethod.setAccessible(true);
|
||||||
|
|
||||||
|
//init new WorldChunkManagerOverworld
|
||||||
|
BiomeLayoutOverworldConfiguration biomeconfig = new BiomeLayoutOverworldConfiguration(freshNMSWorld.getWorldData())
|
||||||
|
.a((GeneratorSettingsOverworld) originalChunkProvider.getChunkGenerator().getSettings());
|
||||||
|
chunkManager = new WorldChunkManagerOverworld(biomeconfig);
|
||||||
|
|
||||||
|
//replace genLayer
|
||||||
|
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(null, biomeconfig.b(), biomeconfig.c(), (LongFunction) (l -> new FastWorldGenContextArea(seed, l)));
|
||||||
|
genLayerField.set(chunkManager, new FastGenLayer(factory));
|
||||||
|
|
||||||
|
return chunkManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastWorldGenContextArea implements AreaContextTransformed<FastAreaLazy> {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
||||||
|
private final NoiseGeneratorPerlin perlinNoise;
|
||||||
|
private final long magicrandom;
|
||||||
|
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
||||||
|
|
||||||
|
public FastWorldGenContextArea(long seed, long lconst) {
|
||||||
|
this.magicrandom = mix(seed, lconst);
|
||||||
|
this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FastAreaLazy a(AreaTransformer8 var0) {
|
||||||
|
return new FastAreaLazy(sharedAreaMap, var0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(long x, long z) {
|
||||||
|
long l = this.magicrandom;
|
||||||
|
l = LinearCongruentialGenerator.a(l, x);
|
||||||
|
l = LinearCongruentialGenerator.a(l, z);
|
||||||
|
l = LinearCongruentialGenerator.a(l, x);
|
||||||
|
l = LinearCongruentialGenerator.a(l, z);
|
||||||
|
this.map.put(Thread.currentThread().getId(), l);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int a(int y) {
|
||||||
|
long tid = Thread.currentThread().getId();
|
||||||
|
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
||||||
|
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
||||||
|
this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom));
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseGeneratorPerlin b() {
|
||||||
|
return this.perlinNoise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long mix(long seed, long lconst) {
|
||||||
|
long l1 = lconst;
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
long l2 = seed;
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
return l2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastGenLayer extends GenLayer {
|
||||||
|
|
||||||
|
private final FastAreaLazy areaLazy;
|
||||||
|
|
||||||
|
public FastGenLayer(AreaFactory<FastAreaLazy> factory) throws Exception {
|
||||||
|
super(() -> null);
|
||||||
|
this.areaLazy = factory.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeBase a(int x, int z) {
|
||||||
|
BiomeBase biome = IRegistry.BIOME.fromId(this.areaLazy.a(x, z));
|
||||||
|
if (biome == null)
|
||||||
|
return Biomes.b;
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastAreaLazy implements Area {
|
||||||
|
|
||||||
|
private final AreaTransformer8 transformer;
|
||||||
|
//ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context
|
||||||
|
//using a map for each thread worsens the performance significantly due to cache misses (factor 5)
|
||||||
|
private final ConcurrentHashMap<Long, Integer> sharedMap;
|
||||||
|
|
||||||
|
public FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, AreaTransformer8 transformer) {
|
||||||
|
this.sharedMap = sharedMap;
|
||||||
|
this.transformer = transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int a(int x, int z) {
|
||||||
|
long zx = ChunkCoordIntPair.pair(x, z);
|
||||||
|
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RegenNoOpWorldLoadListener implements WorldLoadListener {
|
||||||
|
|
||||||
|
private RegenNoOpWorldLoadListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(ChunkCoordIntPair chunkCoordIntPair) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void b() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,519 @@
|
|||||||
|
package com.sk89q.worldedit.bukkit.adapter.impl.regen;
|
||||||
|
|
||||||
|
import com.boydti.fawe.Fawe;
|
||||||
|
import com.boydti.fawe.beta.IChunkCache;
|
||||||
|
import com.boydti.fawe.beta.IChunkGet;
|
||||||
|
import com.boydti.fawe.bukkit.adapter.mc1_16_1.BukkitGetBlocks_1_16_1;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import com.mojang.serialization.Dynamic;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.sk89q.worldedit.bukkit.adapter.Regenerator;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.LongFunction;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import net.minecraft.server.v1_16_R1.Area;
|
||||||
|
import net.minecraft.server.v1_16_R1.AreaContextTransformed;
|
||||||
|
import net.minecraft.server.v1_16_R1.AreaFactory;
|
||||||
|
import net.minecraft.server.v1_16_R1.AreaTransformer8;
|
||||||
|
import net.minecraft.server.v1_16_R1.BiomeBase;
|
||||||
|
import net.minecraft.server.v1_16_R1.Biomes;
|
||||||
|
import net.minecraft.server.v1_16_R1.Chunk;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkConverter;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkCoordIntPair;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkGenerator;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkGeneratorAbstract;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkProviderFlat;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkProviderServer;
|
||||||
|
import net.minecraft.server.v1_16_R1.ChunkStatus;
|
||||||
|
import net.minecraft.server.v1_16_R1.Convertable;
|
||||||
|
import net.minecraft.server.v1_16_R1.DefinedStructureManager;
|
||||||
|
import net.minecraft.server.v1_16_R1.DynamicOpsNBT;
|
||||||
|
import net.minecraft.server.v1_16_R1.GenLayer;
|
||||||
|
import net.minecraft.server.v1_16_R1.GenLayers;
|
||||||
|
import net.minecraft.server.v1_16_R1.GeneratorSettingBase;
|
||||||
|
import net.minecraft.server.v1_16_R1.GeneratorSettings;
|
||||||
|
import net.minecraft.server.v1_16_R1.GeneratorSettingsFlat;
|
||||||
|
import net.minecraft.server.v1_16_R1.IChunkAccess;
|
||||||
|
import net.minecraft.server.v1_16_R1.IRegistry;
|
||||||
|
import net.minecraft.server.v1_16_R1.IRegistryCustom;
|
||||||
|
import net.minecraft.server.v1_16_R1.LightEngineThreaded;
|
||||||
|
import net.minecraft.server.v1_16_R1.LinearCongruentialGenerator;
|
||||||
|
import net.minecraft.server.v1_16_R1.MinecraftServer;
|
||||||
|
import net.minecraft.server.v1_16_R1.NBTBase;
|
||||||
|
import net.minecraft.server.v1_16_R1.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_16_R1.NoiseGeneratorPerlin;
|
||||||
|
import net.minecraft.server.v1_16_R1.ProtoChunk;
|
||||||
|
import net.minecraft.server.v1_16_R1.RegistryReadOps;
|
||||||
|
import net.minecraft.server.v1_16_R1.ResourceKey;
|
||||||
|
import net.minecraft.server.v1_16_R1.World;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldChunkManager;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldChunkManagerOverworld;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldDataServer;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldDimension;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldLoadListener;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldServer;
|
||||||
|
import net.minecraft.server.v1_16_R1.WorldSettings;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.craftbukkit.v1_16_R1.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_16_R1.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_16_R1.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.generator.BlockPopulator;
|
||||||
|
|
||||||
|
public class Regen_v1_16_R1 extends Regenerator<IChunkAccess, ProtoChunk, Chunk, Regen_v1_16_R1.ChunkStatusWrap> {
|
||||||
|
|
||||||
|
private static final Field serverWorldsField;
|
||||||
|
private static final Field worldPaperConfigField;
|
||||||
|
private static final Field flatBedrockField;
|
||||||
|
private static final Field generatorSettingBaseField;
|
||||||
|
private static final Field generatorSettingFlatField;
|
||||||
|
private static final Field delegateField;
|
||||||
|
private static final Field chunkProviderField;
|
||||||
|
|
||||||
|
//list of chunk stati in correct order without FULL
|
||||||
|
private static final Map<ChunkStatus, Regenerator.Concurrency> chunkStati = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing
|
||||||
|
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||||
|
chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk
|
||||||
|
chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8
|
||||||
|
chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||||
|
chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||||
|
chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||||
|
chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk
|
||||||
|
chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||||
|
serverWorldsField.setAccessible(true);
|
||||||
|
|
||||||
|
Field tmpPaperConfigField = null;
|
||||||
|
Field tmpFlatBedrockField = null;
|
||||||
|
try { //only present on paper
|
||||||
|
tmpPaperConfigField = World.class.getDeclaredField("paperConfig");
|
||||||
|
tmpPaperConfigField.setAccessible(true);
|
||||||
|
|
||||||
|
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||||
|
tmpFlatBedrockField.setAccessible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
tmpPaperConfigField = null;
|
||||||
|
tmpFlatBedrockField = null;
|
||||||
|
}
|
||||||
|
worldPaperConfigField = tmpPaperConfigField;
|
||||||
|
flatBedrockField = tmpFlatBedrockField;
|
||||||
|
|
||||||
|
generatorSettingBaseField = ChunkGeneratorAbstract.class.getDeclaredField("h");
|
||||||
|
generatorSettingBaseField.setAccessible(true);
|
||||||
|
|
||||||
|
generatorSettingFlatField = ChunkProviderFlat.class.getDeclaredField("e");
|
||||||
|
generatorSettingFlatField.setAccessible(true);
|
||||||
|
|
||||||
|
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||||
|
delegateField.setAccessible(true);
|
||||||
|
|
||||||
|
chunkProviderField = WorldServer.class.getDeclaredField("chunkProvider");
|
||||||
|
chunkProviderField.setAccessible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//runtime
|
||||||
|
private WorldServer originalNMSWorld;
|
||||||
|
private ChunkProviderServer originalChunkProvider;
|
||||||
|
private WorldServer freshNMSWorld;
|
||||||
|
private ChunkProviderServer freshChunkProvider;
|
||||||
|
private Convertable.ConversionSession session;
|
||||||
|
private DefinedStructureManager structureManager;
|
||||||
|
private LightEngineThreaded lightEngine;
|
||||||
|
private ChunkGenerator generator;
|
||||||
|
|
||||||
|
private Path tempDir;
|
||||||
|
|
||||||
|
private boolean generateFlatBedrock = false;
|
||||||
|
|
||||||
|
public Regen_v1_16_R1(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||||
|
super(originalBukkitWorld, region, target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean prepare() {
|
||||||
|
this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||||
|
originalChunkProvider = originalNMSWorld.getChunkProvider();
|
||||||
|
if (!(originalChunkProvider instanceof ChunkProviderServer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//flat bedrock? (only on paper)
|
||||||
|
try {
|
||||||
|
generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
seed = options.getSeed().orElse(originalNMSWorld.getSeed());
|
||||||
|
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean initNewWorld() throws Exception {
|
||||||
|
//world folder
|
||||||
|
tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen");
|
||||||
|
|
||||||
|
//prepare for world init (see upstream implementation for reference)
|
||||||
|
org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment();
|
||||||
|
org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator();
|
||||||
|
Convertable convertable = Convertable.a(tempDir);
|
||||||
|
ResourceKey<WorldDimension> worldDimKey = getWorldDimKey(env);
|
||||||
|
session = convertable.c("worldeditregentempworld", worldDimKey);
|
||||||
|
WorldDataServer originalWorldData = originalNMSWorld.worldDataServer;
|
||||||
|
|
||||||
|
MinecraftServer server = originalNMSWorld.getServer().getServer();
|
||||||
|
WorldDataServer levelProperties = (WorldDataServer) server.getSaveData();
|
||||||
|
GeneratorSettings newOpts = GeneratorSettings.a.encodeStart(DynamicOpsNBT.a, levelProperties.getGeneratorSettings()).flatMap(tag -> GeneratorSettings.a.parse(this.recursivelySetSeed(new Dynamic<>(DynamicOpsNBT.a, tag), seed, new HashSet<>()))).result().orElseThrow(() -> new IllegalStateException("Unable to map GeneratorOptions"));
|
||||||
|
WorldSettings newWorldSettings = new WorldSettings("worldeditregentempworld", originalWorldData.b.getGameType(), originalWorldData.b.hardcore, originalWorldData.b.getDifficulty(), originalWorldData.b.e(), originalWorldData.b.getGameRules(), originalWorldData.b.g());
|
||||||
|
WorldDataServer newWorldData = new WorldDataServer(newWorldSettings, newOpts, Lifecycle.stable());
|
||||||
|
|
||||||
|
//init world
|
||||||
|
freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier<WorldServer>) () -> new WorldServer(server, server.executorService, session, newWorldData, originalNMSWorld.getDimensionKey(), originalNMSWorld.getTypeKey(), originalNMSWorld.getDimensionManager(), new RegenNoOpWorldLoadListener(), ((WorldDimension) newOpts.e().a(worldDimKey)).c(), originalNMSWorld.isDebugWorld(), seed, ImmutableList.of(), false, env, gen) {
|
||||||
|
@Override
|
||||||
|
public void doTick(BooleanSupplier booleansupplier) { //no ticking
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
freshNMSWorld.savingDisabled = true;
|
||||||
|
removeWorldFromWorldsMap();
|
||||||
|
newWorldData.checkName(originalNMSWorld.worldDataServer.getName()); //rename to original world name
|
||||||
|
|
||||||
|
freshChunkProvider = new ChunkProviderServer(freshNMSWorld, session, server.getDataFixer(), server.getDefinedStructureManager(), server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, server.isSyncChunkWrites(), new RegenNoOpWorldLoadListener(), () -> server.D().getWorldPersistentData()) {
|
||||||
|
// redirect to our protoChunks list
|
||||||
|
@Override
|
||||||
|
public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) {
|
||||||
|
return getProtoChunkAt(x, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chunkProviderField.set(freshNMSWorld, freshChunkProvider);
|
||||||
|
|
||||||
|
//generator
|
||||||
|
if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) {
|
||||||
|
GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) generatorSettingFlatField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
generator = new ChunkProviderFlat(generatorSettingFlat);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof ChunkGeneratorAbstract) {
|
||||||
|
GeneratorSettingBase generatorSettingBase = (GeneratorSettingBase) generatorSettingBaseField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager();
|
||||||
|
if (chunkManager instanceof WorldChunkManagerOverworld) {
|
||||||
|
chunkManager = fastOverWorldChunkManager(chunkManager);
|
||||||
|
}
|
||||||
|
generator = new ChunkGeneratorAbstract(chunkManager, seed, generatorSettingBase);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) {
|
||||||
|
ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
generator = delegate;
|
||||||
|
} else {
|
||||||
|
System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (originalNMSWorld.generator != null) {
|
||||||
|
// wrap custom world generator
|
||||||
|
generator = new CustomChunkGenerator(freshNMSWorld, generator, originalNMSWorld.generator);
|
||||||
|
generateConcurrent = originalNMSWorld.generator.isParallelCapable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//lets start then
|
||||||
|
structureManager = server.getDefinedStructureManager();
|
||||||
|
lightEngine = freshChunkProvider.getLightEngine();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cleanup() {
|
||||||
|
try {
|
||||||
|
session.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//shutdown chunk provider
|
||||||
|
try {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
try {
|
||||||
|
freshChunkProvider.close(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove world from server
|
||||||
|
try {
|
||||||
|
removeWorldFromWorldsMap();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//delete directory
|
||||||
|
try {
|
||||||
|
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||||
|
return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) {
|
||||||
|
public boolean generateFlatBedrock() {
|
||||||
|
return generateFlatBedrock;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Chunk createChunk(ProtoChunk protoChunk) {
|
||||||
|
return new Chunk(freshNMSWorld, protoChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChunkStatusWrap getFullChunkStatus() {
|
||||||
|
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BlockPopulator> getBlockPopulators() {
|
||||||
|
return originalNMSWorld.getWorld().getPopulators();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void populate(Chunk chunk, Random random, BlockPopulator pop) {
|
||||||
|
pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||||
|
return (chunkX, chunkZ) -> new BukkitGetBlocks_1_16_1(freshNMSWorld, chunkX, chunkZ) {
|
||||||
|
@Override
|
||||||
|
public Chunk ensureLoaded(World nmsWorld, int x, int z) {
|
||||||
|
return getChunkAt(x, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper<IChunkAccess> {
|
||||||
|
|
||||||
|
private final ChunkStatus chunkStatus;
|
||||||
|
|
||||||
|
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||||
|
this.chunkStatus = chunkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int requiredNeigborChunkRadius() {
|
||||||
|
return chunkStatus.f();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return chunkStatus.d();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processChunk(Long xz, List<IChunkAccess> accessibleChunks) {
|
||||||
|
chunkStatus.a(freshNMSWorld,
|
||||||
|
generator,
|
||||||
|
structureManager,
|
||||||
|
lightEngine,
|
||||||
|
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||||
|
accessibleChunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//util
|
||||||
|
private void removeWorldFromWorldsMap() {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
try {
|
||||||
|
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||||
|
map.remove("worldeditregentempworld");
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceKey<WorldDimension> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||||
|
switch (env) {
|
||||||
|
case NETHER:
|
||||||
|
return WorldDimension.THE_NETHER;
|
||||||
|
case THE_END:
|
||||||
|
return WorldDimension.THE_END;
|
||||||
|
case NORMAL:
|
||||||
|
default:
|
||||||
|
return WorldDimension.OVERWORLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dynamic<NBTBase> recursivelySetSeed(Dynamic<NBTBase> dynamic, long seed, Set<Dynamic<NBTBase>> seen) {
|
||||||
|
return !seen.add(dynamic) ? dynamic : dynamic.updateMapValues((pair) -> {
|
||||||
|
if (((Dynamic) pair.getFirst()).asString("").equals("seed")) {
|
||||||
|
return pair.mapSecond((v) -> {
|
||||||
|
return v.createLong(seed);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return ((Dynamic) pair.getSecond()).getValue() instanceof NBTTagCompound ? pair.mapSecond((v) -> {
|
||||||
|
return this.recursivelySetSeed((Dynamic) v, seed, seen);
|
||||||
|
}) : pair;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception {
|
||||||
|
Field legacyBiomeInitLayerField = WorldChunkManagerOverworld.class.getDeclaredField("i");
|
||||||
|
legacyBiomeInitLayerField.setAccessible(true);
|
||||||
|
Field largeBiomesField = WorldChunkManagerOverworld.class.getDeclaredField("j");
|
||||||
|
largeBiomesField.setAccessible(true);
|
||||||
|
Field genLayerField = WorldChunkManagerOverworld.class.getDeclaredField("f");
|
||||||
|
genLayerField.setAccessible(true);
|
||||||
|
Field areaLazyField = GenLayer.class.getDeclaredField("b");
|
||||||
|
areaLazyField.setAccessible(true);
|
||||||
|
Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", boolean.class, int.class, int.class, LongFunction.class);
|
||||||
|
initAreaFactoryMethod.setAccessible(true);
|
||||||
|
|
||||||
|
//init new WorldChunkManagerOverworld
|
||||||
|
boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(chunkManager);
|
||||||
|
boolean largebiomes = largeBiomesField.getBoolean(chunkManager);
|
||||||
|
chunkManager = new WorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes);
|
||||||
|
|
||||||
|
//replace genLayer
|
||||||
|
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l)));
|
||||||
|
genLayerField.set(chunkManager, new FastGenLayer(factory));
|
||||||
|
|
||||||
|
return chunkManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastWorldGenContextArea implements AreaContextTransformed<FastAreaLazy> {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
||||||
|
private final NoiseGeneratorPerlin perlinNoise;
|
||||||
|
private final long magicrandom;
|
||||||
|
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
||||||
|
|
||||||
|
public FastWorldGenContextArea(long seed, long lconst) {
|
||||||
|
this.magicrandom = mix(seed, lconst);
|
||||||
|
this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FastAreaLazy a(AreaTransformer8 var0) {
|
||||||
|
return new FastAreaLazy(sharedAreaMap, var0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(long x, long z) {
|
||||||
|
long l = this.magicrandom;
|
||||||
|
l = LinearCongruentialGenerator.a(l, x);
|
||||||
|
l = LinearCongruentialGenerator.a(l, z);
|
||||||
|
l = LinearCongruentialGenerator.a(l, x);
|
||||||
|
l = LinearCongruentialGenerator.a(l, z);
|
||||||
|
this.map.put(Thread.currentThread().getId(), l);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int a(int y) {
|
||||||
|
long tid = Thread.currentThread().getId();
|
||||||
|
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
||||||
|
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
||||||
|
this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom));
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseGeneratorPerlin b() {
|
||||||
|
return this.perlinNoise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long mix(long seed, long lconst) {
|
||||||
|
long l1 = lconst;
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
long l2 = seed;
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
return l2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastGenLayer extends GenLayer {
|
||||||
|
|
||||||
|
private final FastAreaLazy areaLazy;
|
||||||
|
|
||||||
|
public FastGenLayer(AreaFactory<FastAreaLazy> factory) throws Exception {
|
||||||
|
super(() -> null);
|
||||||
|
this.areaLazy = factory.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeBase a(int x, int z) {
|
||||||
|
BiomeBase biome = IRegistry.BIOME.fromId(this.areaLazy.a(x, z));
|
||||||
|
if (biome == null)
|
||||||
|
return Biomes.b;
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastAreaLazy implements Area {
|
||||||
|
|
||||||
|
private final AreaTransformer8 transformer;
|
||||||
|
//ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context
|
||||||
|
//using a map for each thread worsens the performance significantly due to cache misses (factor 5)
|
||||||
|
private final ConcurrentHashMap<Long, Integer> sharedMap;
|
||||||
|
|
||||||
|
public FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, AreaTransformer8 transformer) {
|
||||||
|
this.sharedMap = sharedMap;
|
||||||
|
this.transformer = transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int a(int x, int z) {
|
||||||
|
long zx = ChunkCoordIntPair.pair(x, z);
|
||||||
|
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RegenNoOpWorldLoadListener implements WorldLoadListener {
|
||||||
|
|
||||||
|
private RegenNoOpWorldLoadListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(ChunkCoordIntPair chunkCoordIntPair) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void b() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,529 @@
|
|||||||
|
package com.sk89q.worldedit.bukkit.adapter.impl.regen;
|
||||||
|
|
||||||
|
import com.boydti.fawe.Fawe;
|
||||||
|
import com.boydti.fawe.beta.IChunkCache;
|
||||||
|
import com.boydti.fawe.beta.IChunkGet;
|
||||||
|
import com.boydti.fawe.bukkit.adapter.mc1_16_2.BukkitGetBlocks_1_16_2;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import com.mojang.serialization.Dynamic;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.sk89q.worldedit.bukkit.adapter.Regenerator;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||||
|
import com.sk89q.worldedit.world.RegenOptions;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.LongFunction;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import net.minecraft.server.v1_16_R2.Area;
|
||||||
|
import net.minecraft.server.v1_16_R2.AreaContextTransformed;
|
||||||
|
import net.minecraft.server.v1_16_R2.AreaFactory;
|
||||||
|
import net.minecraft.server.v1_16_R2.AreaTransformer8;
|
||||||
|
import net.minecraft.server.v1_16_R2.BiomeBase;
|
||||||
|
import net.minecraft.server.v1_16_R2.BiomeRegistry;
|
||||||
|
import net.minecraft.server.v1_16_R2.Chunk;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkConverter;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkCoordIntPair;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkGenerator;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkGeneratorAbstract;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkProviderFlat;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkProviderServer;
|
||||||
|
import net.minecraft.server.v1_16_R2.ChunkStatus;
|
||||||
|
import net.minecraft.server.v1_16_R2.Convertable;
|
||||||
|
import net.minecraft.server.v1_16_R2.DefinedStructureManager;
|
||||||
|
import net.minecraft.server.v1_16_R2.DynamicOpsNBT;
|
||||||
|
import net.minecraft.server.v1_16_R2.GenLayer;
|
||||||
|
import net.minecraft.server.v1_16_R2.GenLayers;
|
||||||
|
import net.minecraft.server.v1_16_R2.GeneratorSettingBase;
|
||||||
|
import net.minecraft.server.v1_16_R2.GeneratorSettings;
|
||||||
|
import net.minecraft.server.v1_16_R2.GeneratorSettingsFlat;
|
||||||
|
import net.minecraft.server.v1_16_R2.IChunkAccess;
|
||||||
|
import net.minecraft.server.v1_16_R2.IRegistry;
|
||||||
|
import net.minecraft.server.v1_16_R2.IRegistryCustom;
|
||||||
|
import net.minecraft.server.v1_16_R2.LightEngineThreaded;
|
||||||
|
import net.minecraft.server.v1_16_R2.LinearCongruentialGenerator;
|
||||||
|
import net.minecraft.server.v1_16_R2.MinecraftServer;
|
||||||
|
import net.minecraft.server.v1_16_R2.NBTBase;
|
||||||
|
import net.minecraft.server.v1_16_R2.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_16_R2.NoiseGeneratorPerlin;
|
||||||
|
import net.minecraft.server.v1_16_R2.ProtoChunk;
|
||||||
|
import net.minecraft.server.v1_16_R2.RegistryReadOps;
|
||||||
|
import net.minecraft.server.v1_16_R2.ResourceKey;
|
||||||
|
import net.minecraft.server.v1_16_R2.World;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldChunkManager;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldChunkManagerOverworld;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldDataServer;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldDimension;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldLoadListener;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldServer;
|
||||||
|
import net.minecraft.server.v1_16_R2.WorldSettings;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.craftbukkit.v1_16_R2.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_16_R2.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_16_R2.generator.CustomChunkGenerator;
|
||||||
|
import org.bukkit.generator.BlockPopulator;
|
||||||
|
|
||||||
|
public class Regen_v1_16_R2 extends Regenerator<IChunkAccess, ProtoChunk, Chunk, Regen_v1_16_R2.ChunkStatusWrap> {
|
||||||
|
|
||||||
|
private static final Field serverWorldsField;
|
||||||
|
private static final Field worldPaperConfigField;
|
||||||
|
private static final Field flatBedrockField;
|
||||||
|
private static final Field generatorSettingBaseSupplierField;
|
||||||
|
private static final Field generatorSettingFlatField;
|
||||||
|
private static final Field delegateField;
|
||||||
|
private static final Field chunkProviderField;
|
||||||
|
|
||||||
|
//list of chunk stati in correct order without FULL
|
||||||
|
private static final Map<ChunkStatus, Regenerator.Concurrency> chunkStati = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
chunkStati.put(ChunkStatus.EMPTY, Regenerator.Concurrency.FULL); // radius -1, does nothing
|
||||||
|
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||||
|
chunkStati.put(ChunkStatus.STRUCTURE_REFERENCES, Regenerator.Concurrency.FULL); // radius 8, but no writes to other chunks, only current chunk
|
||||||
|
chunkStati.put(ChunkStatus.BIOMES, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.NOISE, Regenerator.Concurrency.RADIUS); // radius 8
|
||||||
|
chunkStati.put(ChunkStatus.SURFACE, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||||
|
chunkStati.put(ChunkStatus.LIQUID_CARVERS, Regenerator.Concurrency.NONE); // radius 0, but RADIUS and FULL change results
|
||||||
|
chunkStati.put(ChunkStatus.FEATURES, Regenerator.Concurrency.NONE); // uses unsynchronized maps
|
||||||
|
chunkStati.put(ChunkStatus.LIGHT, Regenerator.Concurrency.FULL); // radius 1, but no writes to other chunks, only current chunk
|
||||||
|
chunkStati.put(ChunkStatus.SPAWN, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
chunkStati.put(ChunkStatus.HEIGHTMAPS, Regenerator.Concurrency.FULL); // radius 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||||
|
serverWorldsField.setAccessible(true);
|
||||||
|
|
||||||
|
Field tmpPaperConfigField = null;
|
||||||
|
Field tmpFlatBedrockField = null;
|
||||||
|
try { //only present on paper
|
||||||
|
tmpPaperConfigField = World.class.getDeclaredField("paperConfig");
|
||||||
|
tmpPaperConfigField.setAccessible(true);
|
||||||
|
|
||||||
|
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||||
|
tmpFlatBedrockField.setAccessible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
tmpPaperConfigField = null;
|
||||||
|
tmpFlatBedrockField = null;
|
||||||
|
}
|
||||||
|
worldPaperConfigField = tmpPaperConfigField;
|
||||||
|
flatBedrockField = tmpFlatBedrockField;
|
||||||
|
|
||||||
|
generatorSettingBaseSupplierField = ChunkGeneratorAbstract.class.getDeclaredField("h");
|
||||||
|
generatorSettingBaseSupplierField.setAccessible(true);
|
||||||
|
|
||||||
|
generatorSettingFlatField = ChunkProviderFlat.class.getDeclaredField("e");
|
||||||
|
generatorSettingFlatField.setAccessible(true);
|
||||||
|
|
||||||
|
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||||
|
delegateField.setAccessible(true);
|
||||||
|
|
||||||
|
chunkProviderField = WorldServer.class.getDeclaredField("chunkProvider");
|
||||||
|
chunkProviderField.setAccessible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//runtime
|
||||||
|
private WorldServer originalNMSWorld;
|
||||||
|
private ChunkProviderServer originalChunkProvider;
|
||||||
|
private WorldServer freshNMSWorld;
|
||||||
|
private ChunkProviderServer freshChunkProvider;
|
||||||
|
private Convertable.ConversionSession session;
|
||||||
|
private DefinedStructureManager structureManager;
|
||||||
|
private LightEngineThreaded lightEngine;
|
||||||
|
private ChunkGenerator generator;
|
||||||
|
|
||||||
|
private Path tempDir;
|
||||||
|
|
||||||
|
private boolean generateFlatBedrock = false;
|
||||||
|
|
||||||
|
public Regen_v1_16_R2(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||||
|
super(originalBukkitWorld, region, target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean prepare() {
|
||||||
|
this.originalNMSWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||||
|
originalChunkProvider = originalNMSWorld.getChunkProvider();
|
||||||
|
if (!(originalChunkProvider instanceof ChunkProviderServer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//flat bedrock? (only on paper)
|
||||||
|
try {
|
||||||
|
generateFlatBedrock = flatBedrockField.getBoolean(worldPaperConfigField.get(originalNMSWorld));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
seed = options.getSeed().orElse(originalNMSWorld.getSeed());
|
||||||
|
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean initNewWorld() throws Exception {
|
||||||
|
//world folder
|
||||||
|
tempDir = java.nio.file.Files.createTempDirectory("WorldEditWorldGen");
|
||||||
|
|
||||||
|
//prepare for world init (see upstream implementation for reference)
|
||||||
|
org.bukkit.World.Environment env = originalBukkitWorld.getEnvironment();
|
||||||
|
org.bukkit.generator.ChunkGenerator gen = originalBukkitWorld.getGenerator();
|
||||||
|
Convertable convertable = Convertable.a(tempDir);
|
||||||
|
ResourceKey<WorldDimension> worldDimKey = getWorldDimKey(env);
|
||||||
|
session = convertable.c("worldeditregentempworld", worldDimKey);
|
||||||
|
WorldDataServer originalWorldData = originalNMSWorld.worldDataServer;
|
||||||
|
|
||||||
|
MinecraftServer server = originalNMSWorld.getServer().getServer();
|
||||||
|
WorldDataServer levelProperties = (WorldDataServer) server.getSaveData();
|
||||||
|
RegistryReadOps<NBTBase> nbtRegOps = RegistryReadOps.a(DynamicOpsNBT.a, server.dataPackResources.h(), IRegistryCustom.b());
|
||||||
|
GeneratorSettings newOpts = GeneratorSettings.a.encodeStart(nbtRegOps, levelProperties.getGeneratorSettings()).flatMap(tag -> GeneratorSettings.a.parse(this.recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()))).result().orElseThrow(() -> new IllegalStateException("Unable to map GeneratorOptions"));
|
||||||
|
WorldSettings newWorldSettings = new WorldSettings("worldeditregentempworld", originalWorldData.b.getGameType(), originalWorldData.b.hardcore, originalWorldData.b.getDifficulty(), originalWorldData.b.e(), originalWorldData.b.getGameRules(), originalWorldData.b.g());
|
||||||
|
WorldDataServer newWorldData = new WorldDataServer(newWorldSettings, newOpts, Lifecycle.stable());
|
||||||
|
|
||||||
|
//init world
|
||||||
|
freshNMSWorld = Fawe.get().getQueueHandler().sync((Supplier<WorldServer>) () -> new WorldServer(server, server.executorService, session, newWorldData, originalNMSWorld.getDimensionKey(), originalNMSWorld.getDimensionManager(), new RegenNoOpWorldLoadListener(), ((WorldDimension) newOpts.d().a(worldDimKey)).c(), originalNMSWorld.isDebugWorld(), seed, ImmutableList.of(), false, env, gen) {
|
||||||
|
@Override
|
||||||
|
public void doTick(BooleanSupplier booleansupplier) { //no ticking
|
||||||
|
}
|
||||||
|
}).get();
|
||||||
|
freshNMSWorld.savingDisabled = true;
|
||||||
|
removeWorldFromWorldsMap();
|
||||||
|
newWorldData.checkName(originalNMSWorld.worldDataServer.getName()); //rename to original world name
|
||||||
|
|
||||||
|
freshChunkProvider = new ChunkProviderServer(freshNMSWorld, session, server.getDataFixer(), server.getDefinedStructureManager(), server.executorService, originalChunkProvider.chunkGenerator, freshNMSWorld.spigotConfig.viewDistance, server.isSyncChunkWrites(), new RegenNoOpWorldLoadListener(), () -> server.E().getWorldPersistentData()) {
|
||||||
|
// redirect to our protoChunks list
|
||||||
|
@Override
|
||||||
|
public IChunkAccess getChunkAt(int x, int z, ChunkStatus chunkstatus, boolean flag) {
|
||||||
|
return getProtoChunkAt(x, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chunkProviderField.set(freshNMSWorld, freshChunkProvider);
|
||||||
|
|
||||||
|
//generator
|
||||||
|
if (originalChunkProvider.getChunkGenerator() instanceof ChunkProviderFlat) {
|
||||||
|
GeneratorSettingsFlat generatorSettingFlat = (GeneratorSettingsFlat) generatorSettingFlatField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
generator = new ChunkProviderFlat(generatorSettingFlat);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof ChunkGeneratorAbstract) {
|
||||||
|
Supplier<GeneratorSettingBase> generatorSettingBaseSupplier = (Supplier<GeneratorSettingBase>) generatorSettingBaseSupplierField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
WorldChunkManager chunkManager = originalChunkProvider.getChunkGenerator().getWorldChunkManager();
|
||||||
|
if (chunkManager instanceof WorldChunkManagerOverworld) {
|
||||||
|
chunkManager = fastOverWorldChunkManager(chunkManager);
|
||||||
|
}
|
||||||
|
generator = new ChunkGeneratorAbstract(chunkManager, seed, generatorSettingBaseSupplier);
|
||||||
|
} else if (originalChunkProvider.getChunkGenerator() instanceof CustomChunkGenerator) {
|
||||||
|
ChunkGenerator delegate = (ChunkGenerator) delegateField.get(originalChunkProvider.getChunkGenerator());
|
||||||
|
generator = delegate;
|
||||||
|
} else {
|
||||||
|
System.out.println("Unsupported generator type " + originalChunkProvider.getChunkGenerator().getClass().getName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (originalNMSWorld.generator != null) {
|
||||||
|
// wrap custom world generator
|
||||||
|
generator = new CustomChunkGenerator(freshNMSWorld, generator, originalNMSWorld.generator);
|
||||||
|
generateConcurrent = originalNMSWorld.generator.isParallelCapable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//lets start then
|
||||||
|
structureManager = server.getDefinedStructureManager();
|
||||||
|
lightEngine = freshChunkProvider.getLightEngine();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cleanup() {
|
||||||
|
try {
|
||||||
|
session.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//shutdown chunk provider
|
||||||
|
try {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
try {
|
||||||
|
freshChunkProvider.close(false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove world from server
|
||||||
|
try {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
removeWorldFromWorldsMap();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//delete directory
|
||||||
|
try {
|
||||||
|
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||||
|
return new ProtoChunk(new ChunkCoordIntPair(x, z), ChunkConverter.a) {
|
||||||
|
public boolean generateFlatBedrock() {
|
||||||
|
return generateFlatBedrock;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Chunk createChunk(ProtoChunk protoChunk) {
|
||||||
|
return new Chunk(freshNMSWorld, protoChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChunkStatusWrap getFullChunkStatus() {
|
||||||
|
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BlockPopulator> getBlockPopulators() {
|
||||||
|
return originalNMSWorld.getWorld().getPopulators();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void populate(Chunk chunk, Random random, BlockPopulator pop) {
|
||||||
|
pop.populate(freshNMSWorld.getWorld(), random, chunk.bukkitChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||||
|
return (chunkX, chunkZ) -> new BukkitGetBlocks_1_16_2(freshNMSWorld, chunkX, chunkZ) {
|
||||||
|
@Override
|
||||||
|
public Chunk ensureLoaded(World nmsWorld, int x, int z) {
|
||||||
|
return getChunkAt(x, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ChunkStatusWrap extends Regenerator.ChunkStatusWrapper<IChunkAccess> {
|
||||||
|
|
||||||
|
private final ChunkStatus chunkStatus;
|
||||||
|
|
||||||
|
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||||
|
this.chunkStatus = chunkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int requiredNeigborChunkRadius() {
|
||||||
|
return chunkStatus.f();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return chunkStatus.d();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processChunk(Long xz, List<IChunkAccess> accessibleChunks) {
|
||||||
|
chunkStatus.a(freshNMSWorld,
|
||||||
|
generator,
|
||||||
|
structureManager,
|
||||||
|
lightEngine,
|
||||||
|
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||||
|
accessibleChunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//util
|
||||||
|
private void removeWorldFromWorldsMap() {
|
||||||
|
Fawe.get().getQueueHandler().sync(() -> {
|
||||||
|
try {
|
||||||
|
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||||
|
map.remove("worldeditregentempworld");
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceKey<WorldDimension> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||||
|
switch (env) {
|
||||||
|
case NETHER:
|
||||||
|
return WorldDimension.THE_NETHER;
|
||||||
|
case THE_END:
|
||||||
|
return WorldDimension.THE_END;
|
||||||
|
case NORMAL:
|
||||||
|
default:
|
||||||
|
return WorldDimension.OVERWORLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dynamic<NBTBase> recursivelySetSeed(Dynamic<NBTBase> dynamic, long seed, Set<Dynamic<NBTBase>> seen) {
|
||||||
|
return !seen.add(dynamic) ? dynamic : dynamic.updateMapValues((pair) -> {
|
||||||
|
if (((Dynamic) pair.getFirst()).asString("").equals("seed")) {
|
||||||
|
return pair.mapSecond((v) -> {
|
||||||
|
return v.createLong(seed);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return ((Dynamic) pair.getSecond()).getValue() instanceof NBTTagCompound ? pair.mapSecond((v) -> {
|
||||||
|
return this.recursivelySetSeed((Dynamic) v, seed, seen);
|
||||||
|
}) : pair;
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldChunkManager fastOverWorldChunkManager(WorldChunkManager chunkManager) throws Exception {
|
||||||
|
Field legacyBiomeInitLayerField = WorldChunkManagerOverworld.class.getDeclaredField("i");
|
||||||
|
legacyBiomeInitLayerField.setAccessible(true);
|
||||||
|
Field largeBiomesField = WorldChunkManagerOverworld.class.getDeclaredField("j");
|
||||||
|
largeBiomesField.setAccessible(true);
|
||||||
|
Field biomeRegistryField = WorldChunkManagerOverworld.class.getDeclaredField("k");
|
||||||
|
biomeRegistryField.setAccessible(true);
|
||||||
|
Field genLayerField = WorldChunkManagerOverworld.class.getDeclaredField("f");
|
||||||
|
genLayerField.setAccessible(true);
|
||||||
|
Field areaLazyField = GenLayer.class.getDeclaredField("b");
|
||||||
|
areaLazyField.setAccessible(true);
|
||||||
|
Method initAreaFactoryMethod = GenLayers.class.getDeclaredMethod("a", boolean.class, int.class, int.class, LongFunction.class);
|
||||||
|
initAreaFactoryMethod.setAccessible(true);
|
||||||
|
|
||||||
|
//init new WorldChunkManagerOverworld
|
||||||
|
boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(chunkManager);
|
||||||
|
boolean largebiomes = largeBiomesField.getBoolean(chunkManager);
|
||||||
|
IRegistry<BiomeBase> biomeRegistry = (IRegistry<BiomeBase>) biomeRegistryField.get(chunkManager);
|
||||||
|
chunkManager = new WorldChunkManagerOverworld(seed, legacyBiomeInitLayer, largebiomes, biomeRegistry);
|
||||||
|
|
||||||
|
//replace genLayer
|
||||||
|
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(null, legacyBiomeInitLayer, largebiomes ? 6 : 4, 4, (LongFunction) (l -> new FastWorldGenContextArea(seed, l)));
|
||||||
|
genLayerField.set(chunkManager, new FastGenLayer(factory));
|
||||||
|
|
||||||
|
return chunkManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastWorldGenContextArea implements AreaContextTransformed<FastAreaLazy> {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
||||||
|
private final NoiseGeneratorPerlin perlinNoise;
|
||||||
|
private final long magicrandom;
|
||||||
|
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
||||||
|
|
||||||
|
public FastWorldGenContextArea(long seed, long lconst) {
|
||||||
|
this.magicrandom = mix(seed, lconst);
|
||||||
|
this.perlinNoise = new NoiseGeneratorPerlin(new Random(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FastAreaLazy a(AreaTransformer8 var0) {
|
||||||
|
return new FastAreaLazy(sharedAreaMap, var0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(long x, long z) {
|
||||||
|
long l = this.magicrandom;
|
||||||
|
l = LinearCongruentialGenerator.a(l, x);
|
||||||
|
l = LinearCongruentialGenerator.a(l, z);
|
||||||
|
l = LinearCongruentialGenerator.a(l, x);
|
||||||
|
l = LinearCongruentialGenerator.a(l, z);
|
||||||
|
this.map.put(Thread.currentThread().getId(), l);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int a(int y) {
|
||||||
|
long tid = Thread.currentThread().getId();
|
||||||
|
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
||||||
|
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
||||||
|
this.map.put(tid, LinearCongruentialGenerator.a(e, this.magicrandom));
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseGeneratorPerlin b() {
|
||||||
|
return this.perlinNoise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long mix(long seed, long lconst) {
|
||||||
|
long l1 = lconst;
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
l1 = LinearCongruentialGenerator.a(l1, lconst);
|
||||||
|
long l2 = seed;
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
l2 = LinearCongruentialGenerator.a(l2, l1);
|
||||||
|
return l2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastGenLayer extends GenLayer {
|
||||||
|
|
||||||
|
private final FastAreaLazy areaLazy;
|
||||||
|
|
||||||
|
public FastGenLayer(AreaFactory<FastAreaLazy> factory) throws Exception {
|
||||||
|
super(() -> null);
|
||||||
|
this.areaLazy = factory.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeBase a(IRegistry<BiomeBase> registry, int x, int z) {
|
||||||
|
ResourceKey<BiomeBase> key = BiomeRegistry.a(this.areaLazy.a(x, z));
|
||||||
|
if (key == null)
|
||||||
|
return registry.a(BiomeRegistry.a(0));
|
||||||
|
BiomeBase biome = registry.a(key);
|
||||||
|
if (biome == null)
|
||||||
|
return registry.a(BiomeRegistry.a(0));
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FastAreaLazy implements Area {
|
||||||
|
|
||||||
|
private final AreaTransformer8 transformer;
|
||||||
|
//ConcurrentHashMap is 50% faster that Long2IntLinkedOpenHashMap in a syncronized context
|
||||||
|
//using a map for each thread worsens the performance significantly due to cache misses (factor 5)
|
||||||
|
private final ConcurrentHashMap<Long, Integer> sharedMap;
|
||||||
|
|
||||||
|
public FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, AreaTransformer8 transformer) {
|
||||||
|
this.sharedMap = sharedMap;
|
||||||
|
this.transformer = transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int a(int x, int z) {
|
||||||
|
long zx = ChunkCoordIntPair.pair(x, z);
|
||||||
|
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RegenNoOpWorldLoadListener implements WorldLoadListener {
|
||||||
|
|
||||||
|
private RegenNoOpWorldLoadListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(ChunkCoordIntPair chunkCoordIntPair) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(ChunkCoordIntPair chunkCoordIntPair, @Nullable ChunkStatus chunkStatus) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void b() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -616,6 +616,7 @@ public class RegionCommands {
|
|||||||
try {
|
try {
|
||||||
session.setMask((Mask) null);
|
session.setMask((Mask) null);
|
||||||
session.setSourceMask((Mask) null);
|
session.setSourceMask((Mask) null);
|
||||||
|
actor.printInfo(TranslatableComponent.of("fawe.regen.time"));
|
||||||
success = world.regenerate(region, editSession);
|
success = world.regenerate(region, editSession);
|
||||||
} finally {
|
} finally {
|
||||||
session.setMask(mask);
|
session.setMask(mask);
|
||||||
|
@ -186,7 +186,7 @@ public interface Extent extends InputExtent, OutputExtent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) {
|
default boolean regenerateChunk(int x, int z, @Nullable BiomeType type, @Nullable Long seed) {
|
||||||
throw new UnsupportedOperationException("TODO NOT IMPLEMENTED: " + isWorld());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,6 +31,7 @@ import com.sk89q.worldedit.blocks.BaseItemStack;
|
|||||||
import com.sk89q.worldedit.entity.BaseEntity;
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
import com.sk89q.worldedit.entity.Entity;
|
import com.sk89q.worldedit.entity.Entity;
|
||||||
import com.sk89q.worldedit.entity.Player;
|
import com.sk89q.worldedit.entity.Player;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
@ -226,6 +227,10 @@ public class NullWorld extends AbstractWorld {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) {
|
public void sendFakeChunk(@Nullable Player player, ChunkPacket packet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean regenerate(Region region, Extent extent, RegenOptions options) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ import com.sk89q.worldedit.entity.Player;
|
|||||||
import com.sk89q.worldedit.extension.platform.Platform;
|
import com.sk89q.worldedit.extension.platform.Platform;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
import com.sk89q.worldedit.internal.util.DeprecationUtil;
|
||||||
|
import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility;
|
||||||
import com.sk89q.worldedit.math.BlockVector2;
|
import com.sk89q.worldedit.math.BlockVector2;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
@ -218,7 +220,44 @@ public interface World extends Extent, Keyed, IChunkCache<IChunkGet> {
|
|||||||
* @param editSession the {@link EditSession}
|
* @param editSession the {@link EditSession}
|
||||||
* @return true if re-generation was successful
|
* @return true if re-generation was successful
|
||||||
*/
|
*/
|
||||||
boolean regenerate(Region region, EditSession editSession);
|
default boolean regenerate(Region region, EditSession editSession) {
|
||||||
|
return regenerate(region, editSession, RegenOptions.builder().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerate an area.
|
||||||
|
*
|
||||||
|
* @param region the region
|
||||||
|
* @param extent the {@link Extent}
|
||||||
|
* @return true if re-generation was successful
|
||||||
|
*/
|
||||||
|
default boolean regenerate(Region region, Extent extent) {
|
||||||
|
return regenerate(region, extent, RegenOptions.builder().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerate an area.
|
||||||
|
*
|
||||||
|
* @param region the region
|
||||||
|
* @param extent the {@link Extent}
|
||||||
|
* @param options the regeneration options
|
||||||
|
* @return true if regeneration was successful
|
||||||
|
* @apiNote This must be overridden by new subclasses. See {@link NonAbstractForCompatibility}
|
||||||
|
* for details
|
||||||
|
*/
|
||||||
|
@NonAbstractForCompatibility(
|
||||||
|
delegateName = "regenerate",
|
||||||
|
delegateParams = { Region.class, EditSession.class }
|
||||||
|
)
|
||||||
|
default boolean regenerate(Region region, Extent extent, RegenOptions options) {
|
||||||
|
DeprecationUtil.checkDelegatingOverride(getClass());
|
||||||
|
if (extent instanceof EditSession) {
|
||||||
|
return regenerate(region, (EditSession) extent);
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("This World class ("
|
||||||
|
+ getClass().getName()
|
||||||
|
+ ") does not implement the general Extent variant of this method");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a tree at the given position.
|
* Generate a tree at the given position.
|
||||||
|
@ -165,6 +165,8 @@
|
|||||||
"fawe.tips.tip.biome.pattern": "Tip: The #biome[forest] pattern can be used in any command",
|
"fawe.tips.tip.biome.pattern": "Tip: The #biome[forest] pattern can be used in any command",
|
||||||
"fawe.tips.tip.biome.mask": "Tip: Restrict to a biome with the `$jungle` mask",
|
"fawe.tips.tip.biome.mask": "Tip: Restrict to a biome with the `$jungle` mask",
|
||||||
|
|
||||||
|
"fawe.regen.time": "Regenerating region, this might take a while!",
|
||||||
|
|
||||||
"worldedit.expand.description.vert": "Vertically expand the selection to world limits.",
|
"worldedit.expand.description.vert": "Vertically expand the selection to world limits.",
|
||||||
"worldedit.expand.expanded": "Region expanded {0} blocks",
|
"worldedit.expand.expanded": "Region expanded {0} blocks",
|
||||||
"worldedit.expand.expanded.vert": "Region expanded {0} blocks (top-to-bottom).",
|
"worldedit.expand.expanded.vert": "Region expanded {0} blocks (top-to-bottom).",
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren