From 60a3994d628d6d19c2983582fee4a70a35e65398 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 9 Sep 2023 16:07:29 +0200 Subject: [PATCH] Clean up some regen code (#2405) --- .../v1_17_R1_2/regen/PaperweightRegen.java | 7 +- .../fawe/v1_18_R2/regen/PaperweightRegen.java | 7 +- .../fawe/v1_19_R3/regen/PaperweightRegen.java | 7 +- .../fawe/v1_20_R1/regen/PaperweightRegen.java | 7 +- .../bukkit/adapter/Regenerator.java | 184 +++++++++--------- 5 files changed, 98 insertions(+), 114 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java index 3d9694af9..4d7a749de 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java @@ -184,9 +184,6 @@ public class PaperweightRegen extends Regenerator super.chunkStati.put(new ChunkStatusWrap(s), c)); + chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c)); return true; } @@ -680,7 +677,7 @@ public class PaperweightRegen extends Regenerator processChunk(Long xz, List accessibleChunks) { + public CompletableFuture processChunk(List accessibleChunks) { return chunkStatus.generate( Runnable::run, // TODO revisit, we might profit from this somehow? freshWorld, diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java index 51bd22bdb..34e18f748 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java @@ -178,9 +178,6 @@ public class PaperweightRegen extends Regenerator super.chunkStati.put(new ChunkStatusWrap(s), c)); + chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c)); return true; } @@ -523,7 +520,7 @@ public class PaperweightRegen extends Regenerator processChunk(Long xz, List accessibleChunks) { + public CompletableFuture processChunk(List accessibleChunks) { return chunkStatus.generate( Runnable::run, // TODO revisit, we might profit from this somehow? freshWorld, diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java index 4f22e8734..22bae54fe 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java @@ -192,9 +192,6 @@ public class PaperweightRegen extends Regenerator super.chunkStati.put(new ChunkStatusWrap(s), c)); + chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c)); return true; } @@ -554,7 +551,7 @@ public class PaperweightRegen extends Regenerator processChunk(Long xz, List accessibleChunks) { + public CompletableFuture processChunk(List accessibleChunks) { return chunkStatus.generate( Runnable::run, // TODO revisit, we might profit from this somehow? freshWorld, diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java index b5d5a2733..812cf1d4c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java @@ -192,9 +192,6 @@ public class PaperweightRegen extends Regenerator super.chunkStati.put(new ChunkStatusWrap(s), c)); + chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c)); return true; } @@ -555,7 +552,7 @@ public class PaperweightRegen extends Regenerator processChunk(Long xz, List accessibleChunks) { + public CompletableFuture processChunk(List accessibleChunks) { return chunkStatus.generate( Runnable::run, // TODO revisit, we might profit from this somehow? freshWorld, diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index 7c00ebb2b..d0d9bb652 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -22,14 +22,14 @@ import com.sk89q.worldedit.world.block.BaseBlock; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; import org.apache.logging.log4j.Logger; -import org.bukkit.World; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.WorldInfo; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; @@ -42,7 +42,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.Function; -import java.util.stream.Collectors; /** * Represents an abstract regeneration handler. @@ -62,7 +61,7 @@ public abstract class Regenerator chunkStati = new LinkedHashMap<>(); + protected final Map chunkStatuses = new LinkedHashMap<>(); private final Long2ObjectLinkedOpenHashMap protoChunks = new Long2ObjectLinkedOpenHashMap<>(); private final Long2ObjectOpenHashMap chunks = new Long2ObjectOpenHashMap<>(); protected boolean generateConcurrent = true; @@ -85,19 +84,19 @@ public abstract class Regenerator> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>(); - chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> { + Int2ObjectOpenHashMap chunkCoordsForRadius = new Int2ObjectOpenHashMap<>(); + chunkStatuses.keySet().stream().mapToInt(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> { if (radius == -1) { //ignore ChunkStatus.EMPTY return; } @@ -186,19 +185,19 @@ public abstract class Regenerator>> worldlimits = new Int2ObjectOpenHashMap<>(); - chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> { + Int2ObjectOpenHashMap>> worldLimits = new Int2ObjectOpenHashMap<>(); + chunkStatuses.keySet().stream().mapToInt(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> { if (radius == -1) { //ignore ChunkStatus.EMPTY return; } Long2ObjectOpenHashMap> map = new Long2ObjectOpenHashMap<>(); - for (Long xz : chunkCoordsForRadius.get(radius)) { + for (long xz : chunkCoordsForRadius.get(radius)) { int x = MathMan.unpairIntX(xz); int z = MathMan.unpairIntY(xz); List l = new ArrayList<>((radius + 1 + radius) * (radius + 1 + radius)); @@ -209,80 +208,63 @@ public abstract class Regenerator entry : chunkStati.entrySet()) { + for (Map.Entry entry : chunkStatuses.entrySet()) { ChunkStatus chunkStatus = entry.getKey(); int radius = chunkStatus.requiredNeighborChunkRadius0(); - List coords = chunkCoordsForRadius.get(radius); + long[] coords = chunkCoordsForRadius.get(radius); + Long2ObjectOpenHashMap> limitsForRadius = worldLimits.get(radius); if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) { - SequentialTasks>> tasks = getChunkStatusTaskRows(coords, radius); - for (ConcurrentTasks> para : tasks) { + SequentialTasks> tasks = getChunkStatusTaskRows(coords, radius); + for (ConcurrentTasks para : tasks) { List scheduled = new ArrayList<>(tasks.size()); - for (SequentialTasks row : para) { + for (LongList row : para) { scheduled.add(() -> { - for (Long xz : row) { - chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); + for (long xz : row) { + chunkStatus.processChunkSave(xz, limitsForRadius.get(xz)); } }); } - try { - List> futures = new ArrayList<>(); - scheduled.forEach(task -> futures.add(executor.submit(task))); - for (Future future : futures) { - future.get(); - } - } catch (Exception e) { - e.printStackTrace(); - } + runAndWait(scheduled); } } else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) { // every chunk can be processed individually - List scheduled = new ArrayList<>(coords.size()); + List scheduled = new ArrayList<>(coords.length); for (long xz : coords) { - scheduled.add(() -> { - chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); - }); - } - try { - List> futures = new ArrayList<>(); - scheduled.forEach(task -> futures.add(executor.submit(task))); - for (Future future : futures) { - future.get(); - } - } catch (Exception e) { - e.printStackTrace(); + scheduled.add(() -> chunkStatus.processChunkSave(xz, limitsForRadius.get(xz))); } + runAndWait(scheduled); } else { // Concurrency.NONE or generateConcurrent == false // run sequential but submit to different thread // running regen on the main thread otherwise triggers async-only events on the main thread executor.submit(() -> { for (long xz : coords) { - chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); + chunkStatus.processChunkSave(xz, limitsForRadius.get(xz)); } }).get(); // wait until finished this step } } //convert to proper chunks - for (Long xz : chunkCoordsForRadius.get(0)) { + 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! + for (long xz : chunkCoordsForRadius.get(0)) { //FULL.requiredNeighbourChunkRadius() == 0! Chunk chunk = chunks.get(xz); - FULL.processChunkSave(xz, Arrays.asList(chunk)); + FULL.processChunkSave(xz, List.of(chunk)); } //populate List populators = getBlockPopulators(); - for (Long xz : chunkCoordsForRadius.get(0)) { + for (long xz : chunkCoordsForRadius.get(0)) { int x = MathMan.unpairIntX(xz); int z = MathMan.unpairIntY(xz); @@ -302,6 +284,18 @@ public abstract class Regenerator tasks) { + try { + List> futures = new ArrayList<>(); + tasks.forEach(task -> futures.add(executor.submit(task))); + for (Future future : futures) { + future.get(); + } + } catch (Exception e) { + LOGGER.catching(e); + } + } + private void copyToWorld() { //Setting Blocks boolean genbiomes = options.shouldRegenBiomes(); @@ -437,7 +431,7 @@ public abstract class Regenerator initSourceQueueCache(); //algorithms - private List getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks + private 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, @@ -455,76 +449,79 @@ public abstract class Regenerator MathMan.pairInt(c.getX(), c.getZ())) - .collect(Collectors.toList()); + .mapToLong(c -> MathMan.pairInt(c.getX(), c.getZ())) + .toArray(); } /** * Creates a list of chunkcoord rows that may be executed concurrently * - * @param allcoords the coords that should be sorted into rows, must be sorted by z and x + * @param allCoords the coords that should be sorted into rows, must be sorted by z and x * @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus * .requiredNeighborRadius) * @return a list of chunkcoords rows that may be executed concurrently */ - private SequentialTasks>> getChunkStatusTaskRows( - List allcoords, + private SequentialTasks> getChunkStatusTaskRows( + long[] allCoords, int requiredNeighborChunkRadius ) { - int requiredneighbors = Math.max(0, 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>> tasks; - if (maxz - minz > maxx - minx) { - int numlists = Math.min(requiredneighbors * 2 + 1, maxx - minx + 1); + final int coordsCount = allCoords.length; + long first = coordsCount == 0 ? 0 : allCoords[0]; + long last = coordsCount == 0 ? 0 : allCoords[coordsCount - 1]; + int minX = MathMan.unpairIntX(first); + int maxX = MathMan.unpairIntX(last); + int minZ = MathMan.unpairIntY(first); + int maxZ = MathMan.unpairIntY(last); + SequentialTasks> tasks; + if (maxZ - minZ > maxX - minX) { + int numlists = Math.min(requiredNeighbors * 2 + 1, maxX - minX + 1); - Int2ObjectOpenHashMap> byx = new Int2ObjectOpenHashMap(); - int expectedListLength = (allcoords.size() + 1) / (maxx - minx); + Int2ObjectOpenHashMap byX = new Int2ObjectOpenHashMap<>(); + int expectedListLength = (coordsCount + 1) / (maxX - minX); //init lists - for (int i = minx; i <= maxx; i++) { - byx.put(i, new SequentialTasks(expectedListLength)); + for (int i = minX; i <= maxX; i++) { + byX.put(i, new LongArrayList(expectedListLength)); } //sort into lists by x coord - for (Long xz : allcoords) { - byx.get(MathMan.unpairIntX(xz)).add(xz); + for (long allCoord : allCoords) { + byX.get(MathMan.unpairIntX(allCoord)).add(allCoord); } //create parallel tasks - tasks = new SequentialTasks(numlists); + tasks = new SequentialTasks<>(numlists); for (int offset = 0; offset < numlists; offset++) { - ConcurrentTasks> para = new ConcurrentTasks((maxz - minz + 1) / numlists + 1); - for (int i = 0; minx + i * numlists + offset <= maxx; i++) { - para.add(byx.get(minx + i * numlists + offset)); + ConcurrentTasks para = new ConcurrentTasks<>((maxZ - minZ + 1) / numlists + 1); + for (int i = 0; minX + i * numlists + offset <= maxX; i++) { + para.add(byX.get(minX + i * numlists + offset)); } tasks.add(para); } } else { - int numlists = Math.min(requiredneighbors * 2 + 1, maxz - minz + 1); + int numlists = Math.min(requiredNeighbors * 2 + 1, maxZ - minZ + 1); - Int2ObjectOpenHashMap> byz = new Int2ObjectOpenHashMap(); - int expectedListLength = (allcoords.size() + 1) / (maxz - minz); + Int2ObjectOpenHashMap byZ = new Int2ObjectOpenHashMap<>(); + int expectedListLength = (coordsCount + 1) / (maxZ - minZ); //init lists - for (int i = minz; i <= maxz; i++) { - byz.put(i, new SequentialTasks(expectedListLength)); + for (int i = minZ; i <= maxZ; i++) { + byZ.put(i, new LongArrayList(expectedListLength)); } //sort into lists by x coord - for (Long xz : allcoords) { - byz.get(MathMan.unpairIntY(xz)).add(xz); + for (long allCoord : allCoords) { + byZ.get(MathMan.unpairIntY(allCoord)).add(allCoord); } //create parallel tasks - tasks = new SequentialTasks(numlists); + tasks = new SequentialTasks<>(numlists); for (int offset = 0; offset < numlists; offset++) { - ConcurrentTasks> para = new ConcurrentTasks((maxx - minx + 1) / numlists + 1); - for (int i = 0; minz + i * numlists + offset <= maxz; i++) { - para.add(byz.get(minz + i * numlists + offset)); + ConcurrentTasks para = new ConcurrentTasks<>((maxX - minX + 1) / numlists + 1); + for (int i = 0; minZ + i * numlists + offset <= maxZ; i++) { + para.add(byZ.get(minZ + i * numlists + offset)); } tasks.add(para); } @@ -576,15 +573,14 @@ public abstract class Regenerator processChunk(Long xz, List accessibleChunks); + public abstract CompletableFuture processChunk(List accessibleChunks); - void processChunkSave(Long xz, List accessibleChunks) { + void processChunkSave(long xz, List accessibleChunks) { try { - processChunk(xz, accessibleChunks).get(); + processChunk(accessibleChunks).get(); } catch (Exception e) { LOGGER.error( "Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz), @@ -597,16 +593,16 @@ public abstract class Regenerator extends Tasks { - public SequentialTasks(int expectedsize) { - super(expectedsize); + public SequentialTasks(int expectedSize) { + super(expectedSize); } } public static class ConcurrentTasks extends Tasks { - public ConcurrentTasks(int expectedsize) { - super(expectedsize); + public ConcurrentTasks(int expectedSize) { + super(expectedSize); } } @@ -615,8 +611,8 @@ public abstract class Regenerator tasks; - public Tasks(int expectedsize) { - tasks = new ArrayList(expectedsize); + public Tasks(int expectedSize) { + tasks = new ArrayList<>(expectedSize); } public void add(T task) {