Slightly improve regen workflow, update adapters to a9c745a600

This commit is contained in:
dordsor21 2021-09-16 17:42:31 +01:00
parent e4cbd85197
commit 4182d7473c
No known key found for this signature in database
GPG Key ID: 1E53E88969FFCF0B
3 changed files with 43 additions and 39 deletions

View File

@ -27,7 +27,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -52,11 +52,10 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
//runtime //runtime
protected final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>(); protected final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
protected boolean generateConcurrent = true;
protected long seed;
private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>(); private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>(); private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
protected boolean generateConcurrent = true;
protected long seed;
private ExecutorService executor; private ExecutorService executor;
private SingleThreadQueueExtent source; private SingleThreadQueueExtent source;
@ -75,6 +74,15 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
this.options = options; this.options = options;
} }
private static Random getChunkRandom(long worldseed, int x, int z) {
Random random = new Random();
random.setSeed(worldseed);
long xRand = random.nextLong() / 2L * 2L + 1L;
long zRand = random.nextLong() / 2L * 2L + 1L;
random.setSeed((long) x * xRand + (long) z * zRand ^ worldseed);
return random;
}
/** /**
* Regenerates the selected {@code Region}. * Regenerates the selected {@code Region}.
* *
@ -145,7 +153,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
executor = Executors.newFixedThreadPool(Settings.IMP.QUEUE.PARALLEL_THREADS); executor = Executors.newFixedThreadPool(Settings.IMP.QUEUE.PARALLEL_THREADS);
} // else using sequential chunk generation, concurrent not supported } // else using sequential chunk generation, concurrent not supported
//TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)? //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 //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 //generate chunk coords lists with a certain radius
@ -154,7 +162,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
if (radius == -1) { //ignore ChunkStatus.EMPTY if (radius == -1) { //ignore ChunkStatus.EMPTY
return; 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 int border = 10 - 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)); chunkCoordsForRadius.put(radius, getChunkCoordsRegen(region, border));
}); });
@ -185,7 +194,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
worldlimits.put(radius, map); worldlimits.put(radius, map);
}); });
//run generation tasks exluding FULL chunk status //run generation tasks excluding FULL chunk status
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStati.entrySet()) { for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStati.entrySet()) {
ChunkStatus chunkStatus = entry.getKey(); ChunkStatus chunkStatus = entry.getKey();
int radius = chunkStatus.requiredNeigborChunkRadius0(); int radius = chunkStatus.requiredNeigborChunkRadius0();
@ -194,18 +203,18 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) { if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks = getChunkStatusTaskRows(coords, radius); SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks = getChunkStatusTaskRows(coords, radius);
for (ConcurrentTasks<SequentialTasks<Long>> para : tasks) { for (ConcurrentTasks<SequentialTasks<Long>> para : tasks) {
List scheduled = new ArrayList<>(tasks.size()); List<Runnable> scheduled = new ArrayList<>(tasks.size());
for (SequentialTasks<Long> row : para) { for (SequentialTasks<Long> row : para) {
scheduled.add((Callable) () -> { scheduled.add(() -> {
for (Long xz : row) { for (Long xz : row) {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
} }
return null;
}); });
} }
try { try {
List<Future> futures = executor.invokeAll(scheduled); List<Future<?>> futures = new ArrayList<>();
for (Future future : futures) { scheduled.forEach(task -> futures.add(executor.submit(task)));
for (Future<?> future : futures) {
future.get(); future.get();
} }
} catch (Exception e) { } catch (Exception e) {
@ -214,16 +223,16 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
} }
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) { } else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
// every chunk can be processed individually // every chunk can be processed individually
List scheduled = new ArrayList(coords.size()); List<Runnable> scheduled = new ArrayList<>(coords.size());
for (long xz : coords) { for (long xz : coords) {
scheduled.add((Callable) () -> { scheduled.add(() -> {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
return null;
}); });
} }
try { try {
List<Future> futures = executor.invokeAll(scheduled); List<Future<?>> futures = new ArrayList<>();
for (Future future : futures) { scheduled.forEach(task -> futures.add(executor.submit(task)));
for (Future<?> future : futures) {
future.get(); future.get();
} }
} catch (Exception e) { } catch (Exception e) {
@ -318,13 +327,13 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
*/ */
protected abstract boolean initNewWorld() throws Exception; protected abstract boolean initNewWorld() throws Exception;
//functions to implement by sub class - regenate related
/** /**
* Implement the cleanup of all the mess that is created during the regeneration process (initNewWorld() and generate()).This function must not throw any exceptions. * 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(); protected abstract void cleanup();
//functions to implement by sub class - regenate related
/** /**
* Implement the initialization of a {@code ProtoChunk} here. * Implement the initialization of a {@code ProtoChunk} here.
* *
@ -391,8 +400,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
return adjustedRegion.getChunks().stream() return adjustedRegion.getChunks().stream()
.map(c -> BlockVector2.at(c.getX(), c.getZ())) .map(c -> BlockVector2.at(c.getX(), c.getZ()))
.sorted(Comparator .sorted(Comparator
.<BlockVector2>comparingInt(c -> c.getZ()) .comparingInt(BlockVector2::getZ)
.thenComparingInt(c -> c.getX())) //needed for RegionLimitedWorldAccess .thenComparingInt(BlockVector2::getX)) //needed for RegionLimitedWorldAccess
.map(c -> MathMan.pairInt(c.getX(), c.getZ())) .map(c -> MathMan.pairInt(c.getX(), c.getZ()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -401,7 +410,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
* Creates a list of chunkcoord rows that may be executed concurrently * 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 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) * @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus
* .requiredNeighborRadius)
* @return a list of chunkcoords rows that may be executed concurrently * @return a list of chunkcoords rows that may be executed concurrently
*/ */
private SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> getChunkStatusTaskRows( private SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> getChunkStatusTaskRows(
@ -470,17 +480,14 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
return tasks; 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 //classes
public enum Concurrency {
FULL,
RADIUS,
NONE
}
/** /**
* This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step. * This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step.
* *
@ -513,11 +520,11 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
* @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}. * @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}.
* This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently. * 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); public abstract CompletableFuture<?> processChunk(Long xz, List<IChunkAccess> accessibleChunks);
void processChunkSave(Long xz, List<IChunkAccess> accessibleChunks) { void processChunkSave(Long xz, List<IChunkAccess> accessibleChunks) {
try { try {
processChunk(xz, accessibleChunks); processChunk(xz, accessibleChunks).get();
} catch (Exception e) { } catch (Exception e) {
LOGGER.error( LOGGER.error(
"Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz), "Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz),
@ -528,12 +535,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
} }
public enum Concurrency {
FULL,
RADIUS,
NONE
}
public static class SequentialTasks<T> extends Tasks<T> { public static class SequentialTasks<T> extends Tasks<T> {
public SequentialTasks(int expectedsize) { public SequentialTasks(int expectedsize) {

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.bukkit;
import com.fastasyncworldedit.bukkit.util.WorldUnloadedException; import com.fastasyncworldedit.bukkit.util.WorldUnloadedException;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
@ -235,6 +236,8 @@ public class BukkitWorld extends AbstractWorld {
} else { } else {
throw new UnsupportedOperationException("Missing BukkitImplAdapter for this version."); throw new UnsupportedOperationException("Missing BukkitImplAdapter for this version.");
} }
} catch (FaweException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
LOGGER.warn("Regeneration via adapter failed.", e); LOGGER.warn("Regeneration via adapter failed.", e);
return false; return false;