From f4eb0cfbcb2e0a0060becac2393cd3fc1023b70f Mon Sep 17 00:00:00 2001 From: Daniel Mills Date: Tue, 29 Dec 2020 06:14:18 -0500 Subject: [PATCH] Writer fixes --- .../com/volmit/iris/generator/IrisEngine.java | 2 - .../volmit/iris/pregen/DirectWorldWriter.java | 6 +- .../com/volmit/iris/pregen/Pregenerator.java | 386 +++++++++++++++++- .../engine/EngineCompositeGenerator.java | 10 +- .../iris/scaffold/engine/IrisAccess.java | 1 + .../scaffold/hunk/io/HunkRegionSlice.java | 13 +- .../iris/scaffold/parallel/MultiBurst.java | 8 + .../java/com/volmit/iris/util/PregenJob.java | 2 +- src/main/java/net/querz/mca/MCAUtil.java | 5 + 9 files changed, 417 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/volmit/iris/generator/IrisEngine.java b/src/main/java/com/volmit/iris/generator/IrisEngine.java index ea8de8c9b..04260a0d4 100644 --- a/src/main/java/com/volmit/iris/generator/IrisEngine.java +++ b/src/main/java/com/volmit/iris/generator/IrisEngine.java @@ -1,7 +1,6 @@ package com.volmit.iris.generator; import com.volmit.iris.Iris; -import com.volmit.iris.IrisSettings; import com.volmit.iris.scaffold.engine.*; import com.volmit.iris.scaffold.hunk.Hunk; import com.volmit.iris.util.J; @@ -109,7 +108,6 @@ public class IrisEngine extends BlockPopulator implements Engine try { boolean structures = postblocks != null; - boolean multicore = !IrisSettings.get().isUseGleamPregenerator(); //TODO: LATER s.acquire(1); PrecisionStopwatch p = PrecisionStopwatch.start(); Hunk blocks = vblocks.synchronize().listen((xx,y,zz,t) -> catchBlockUpdates(x+xx,y+getMinHeight(),z+zz, t)); diff --git a/src/main/java/com/volmit/iris/pregen/DirectWorldWriter.java b/src/main/java/com/volmit/iris/pregen/DirectWorldWriter.java index 69c310d15..f1da8e247 100644 --- a/src/main/java/com/volmit/iris/pregen/DirectWorldWriter.java +++ b/src/main/java/com/volmit/iris/pregen/DirectWorldWriter.java @@ -1,4 +1,4 @@ -package com.volmit.iris.scaffold.engine; +package com.volmit.iris.pregen; import com.volmit.iris.Iris; import com.volmit.iris.nms.INMS; @@ -33,7 +33,7 @@ public class DirectWorldWriter { this.worldFolder = worldFolder; lastUse = SmoothieMap.newBuilder().build(); writeBuffer = new KMap<>(); - new File(worldFolder, "region").mkdirs(); + new File(worldFolder, "iris/mca-region").mkdirs(); } public void flush() @@ -86,7 +86,7 @@ public class DirectWorldWriter { public File getMCAFile(int x, int z) { - return new File(worldFolder, "region/r." + x + "." + z + ".mca"); + return new File(worldFolder, "iris/mca-region/r." + x + "." + z + ".mca"); } public BlockData getBlockData(CompoundTag tag) { diff --git a/src/main/java/com/volmit/iris/pregen/Pregenerator.java b/src/main/java/com/volmit/iris/pregen/Pregenerator.java index 329b0ba33..36af82f03 100644 --- a/src/main/java/com/volmit/iris/pregen/Pregenerator.java +++ b/src/main/java/com/volmit/iris/pregen/Pregenerator.java @@ -1,8 +1,390 @@ package com.volmit.iris.pregen; +import com.volmit.iris.Iris; +import com.volmit.iris.IrisSettings; +import com.volmit.iris.scaffold.engine.IrisAccess; +import com.volmit.iris.scaffold.parallel.BurstExecutor; +import com.volmit.iris.scaffold.parallel.MultiBurst; +import com.volmit.iris.util.*; +import lombok.Data; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; import org.bukkit.event.Listener; -public class MCAPregenerator implements Listener +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +@Data +public class Pregenerator implements Listener { - + private static final Color COLOR_MCA_PREPARE = Color.DARK_GRAY; + private static final Color COLOR_MCA_GENERATE = Color.MAGENTA; + private static final Color COLOR_MCA_GENERATE_SLOW = Color.MAGENTA.darker().darker(); + private static final Color COLOR_MCA_GENERATED = Color.CYAN; + private static final Color COLOR_MCA_SEALED = Color.GREEN; + private static final Color COLOR_MCA_SEALING = Color.GREEN.darker().darker(); + private final World world; + private final DirectWorldWriter directWriter; + private final AtomicBoolean active; + private final AtomicBoolean running; + private final KList onComplete; + private final ChunkPosition max; + private final ChunkPosition min; + private final MCAPregenGui gui; + + public Pregenerator(World world, IrisAccess access, int blockSize, int xoffset, int zoffset) + { + this.world = world; + this.directWriter = new DirectWorldWriter(world.getWorldFolder()); + this.running = new AtomicBoolean(true); + this.active = new AtomicBoolean(true); + MultiBurst burst = new MultiBurst(Runtime.getRuntime().availableProcessors()); + int mcaSize = (((blockSize >> 4) + 2) >> 5) + 2; + int xaoff = (xoffset >> 4) >> 5; + int zaoff = (zoffset >> 4) >> 5; + onComplete = new KList<>(); + max = new ChunkPosition(xoffset + (blockSize/2), zoffset + (blockSize/2)); + min = new ChunkPosition(xoffset - (blockSize/2), zoffset - (blockSize/2)); + gui = IrisSettings.get().isLocalPregenGui() && IrisSettings.get().isUseServerLaunchedGuis() ? MCAPregenGui.createAndShowGUI(this) : null; + Spiraler spiraler = new Spiraler(mcaSize, mcaSize, (xx,zz) -> { + int x = xaoff + xx; + int z = zaoff + zz; + try { + flushWorld(); + drawMCA(x, z, COLOR_MCA_PREPARE); + File mca = new File(world.getWorldFolder(), "region/r." + x + "." + z + ".mca"); + File mcg = directWriter.getMCAFile(x, z); + Path fileToMovePath = Paths.get(mcg.toURI()); + Path targetPath = Paths.get(mca.toURI()); + BurstExecutor e = burst.burst(1024); + int mcaox = x << 5; + int mcaoz = z << 5; + + if(isMCAWritable(x,z) && !mcg.exists()) + { + for(int i = 0; i < 32; i++) + { + int ii = i; + for(int j = 0; j < 32; j++) + { + int jj = j; + e.queue(() -> { + draw(ii + mcaox, jj + mcaoz, COLOR_MCA_GENERATE); + access.directWriteChunk(world, ii + mcaox, jj + mcaoz, directWriter); + draw(ii + mcaox, jj + mcaoz, COLOR_MCA_GENERATED); + }); + } + } + + directWriter.flush(); + Files.move(fileToMovePath, targetPath); + } + + else + { + for(int i = 0; i < 32; i++) + { + int ii = i; + for(int j = 0; j < 32; j++) + { + int jj = j; + e.queue(() -> { + draw(ii + mcaox, jj + mcaoz, COLOR_MCA_GENERATE_SLOW); + access.generatePaper(world, ii+mcaox, jj + mcaoz); + draw(ii + mcaox, jj + mcaoz, COLOR_MCA_GENERATED); + }); + } + } + } + + e.complete(); + drawMCA(x, z, COLOR_MCA_SEALED); + flushWorld(); + drawMCA(x, z, COLOR_MCA_SEALED); + } catch (IOException e) { + e.printStackTrace(); + } + }); + new Thread(() -> { + while(running.get() && spiraler.hasNext()) + { + if(active.get()) + { + spiraler.next(); + } + } + + burst.shutdownNow(); + directWriter.flush(); + flushWorld(); + onComplete.forEach(Runnable::run); + + if(gui != null) + { + gui.close(); + } + }); + } + + public void shutdown() + { + running.set(false); + active.set(false); + } + + private void draw(int cx, int cz, Color color) + { + if(gui != null) + { + gui.func.accept(new ChunkPosition(cx, cz), color); + } + } + + private void drawMCA(int cx, int cz, Color color) + { + for(int i = 0; i < 32; i++) + { + for(int j = 0; j < 32; j++) + { + draw((cx << 5) + i, (cz << 5) + j, color); + } + } + } + + private void flushWorld() + { + if(Bukkit.isPrimaryThread()) + { + flushWorldSync(); + return; + } + + AtomicBoolean b = new AtomicBoolean(false); + J.s(() -> { + flushWorldSync(); + b.set(true); + }); + + while(!b.get()) + { + J.sleep(10); + } + } + + private void flushWorldSync() + { + for(Chunk i : world.getLoadedChunks()) + { + i.unload(true); + } + + world.save(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "save-all"); + } + + private boolean isMCAWritable(int x, int z) { + File mca = new File(world.getWorldFolder(), "region/r." + x + "." + z + ".mca"); + + if (mca.exists()) { + return false; + } + + for (Chunk i : world.getLoadedChunks()) + { + if(i.getX() >> 5 == x && i.getZ() >> 5 == z) + { + return false; + } + } + + return true; + } + + public String[] getProgress() { + return new String[]{"Derp"}; + } + + public boolean paused() { + return !active.get(); + } + + public static class MCAPregenGui extends JPanel implements KeyListener + { + private Pregenerator job; + private static final long serialVersionUID = 2094606939770332040L; + private KList order = new KList<>(); + private int res = 512; + Graphics2D bg; + private ReentrantLock l; + private BufferedImage image = new BufferedImage(res, res, BufferedImage.TYPE_INT_RGB); + private Consumer2 func; + private JFrame frame; + + public MCAPregenGui() + { + + } + + public void paint(int x, int z, Color c) + { + func.accept(new ChunkPosition(x, z), c); + } + + @Override + public void paint(Graphics gx) + { + Graphics2D g = (Graphics2D) gx; + bg = (Graphics2D) image.getGraphics(); + + l.lock(); + while(order.isNotEmpty()) + { + try + { + order.pop().run(); + } + + catch(Throwable e) + { + + } + } + l.unlock(); + + g.drawImage(image, 0, 0, getParent().getWidth(), getParent().getHeight(), new ImageObserver() + { + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) + { + return true; + } + }); + + g.setColor(Color.WHITE); + g.setFont(new Font("Hevetica", Font.BOLD, 28)); + String[] prog = job.getProgress(); + int h = g.getFontMetrics().getHeight() + 5; + int hh = 20; + + if(job.paused()) + { + g.drawString("PAUSED", 20, hh += h); + + g.drawString("Press P to Resume", 20, hh += h); + } + + else + { + for(String i : prog) + { + g.drawString(i, 20, hh += h); + } + + g.drawString("Press P to Pause", 20, hh += h); + } + + J.sleep((long) (IrisSettings.get().isMaximumPregenGuiFPS() ? 4 : 250)); + repaint(); + } + + private void draw(ChunkPosition p, Color c, Graphics2D bg) + { + double pw = M.lerpInverse(job.getMin().getX(), job.getMax().getX(), p.getX()); + double ph = M.lerpInverse(job.getMin().getZ(), job.getMax().getZ(), p.getZ()); + double pwa = M.lerpInverse(job.getMin().getX(), job.getMax().getX(), p.getX() + 1); + double pha = M.lerpInverse(job.getMin().getZ(), job.getMax().getZ(), p.getZ() + 1); + int x = (int) M.lerp(0, res, pw); + int z = (int) M.lerp(0, res, ph); + int xa = (int) M.lerp(0, res, pwa); + int za = (int) M.lerp(0, res, pha); + bg.setColor(c); + bg.fillRect(x, z, xa - x, za - z); + } + + @SuppressWarnings("deprecation") + private static MCAPregenGui createAndShowGUI(Pregenerator j) + { + JFrame frame = new JFrame("Pregen View"); + MCAPregenGui nv = new MCAPregenGui(); + frame.addKeyListener(nv); + nv.l = new ReentrantLock(); + nv.frame = frame; + nv.job = j; + nv.func = (c, b) -> + { + if(b.equals(Color.pink) && c.equals(new ChunkPosition(Integer.MAX_VALUE, Integer.MAX_VALUE))) + { + frame.hide(); + } + nv.l.lock(); + nv.order.add(() -> nv.draw(c, b, nv.bg)); + nv.l.unlock(); + }; + frame.add(nv); + frame.setSize(1000, 1000); + frame.setVisible(true); + File file = Iris.getCached("Iris Icon", "https://raw.githubusercontent.com/VolmitSoftware/Iris/master/icon.png"); + + if(file != null) + { + try + { + frame.setIconImage(ImageIO.read(file)); + } + + catch(IOException e) + { + + } + } + + return nv; + } + + public static void launch(Pregenerator g) + { + J.a(() -> + { + createAndShowGUI(g); + }); + } + + @Override + public void keyTyped(KeyEvent e) + { + + } + + @Override + public void keyPressed(KeyEvent e) + { + + } + + @Override + public void keyReleased(KeyEvent e) + { + if(e.getKeyCode() == KeyEvent.VK_P) + { + PregenJob.pauseResume(); + } + } + + public void close() { + frame.setVisible(false); + } + } } \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/scaffold/engine/EngineCompositeGenerator.java b/src/main/java/com/volmit/iris/scaffold/engine/EngineCompositeGenerator.java index 0185b5b4d..c57ca6aa1 100644 --- a/src/main/java/com/volmit/iris/scaffold/engine/EngineCompositeGenerator.java +++ b/src/main/java/com/volmit/iris/scaffold/engine/EngineCompositeGenerator.java @@ -6,6 +6,7 @@ import com.volmit.iris.generator.IrisEngineCompound; import com.volmit.iris.manager.IrisDataManager; import com.volmit.iris.object.IrisBiome; import com.volmit.iris.object.IrisDimension; +import com.volmit.iris.pregen.DirectWorldWriter; import com.volmit.iris.scaffold.IrisWorlds; import com.volmit.iris.scaffold.cache.Cache; import com.volmit.iris.scaffold.hunk.Hunk; @@ -312,11 +313,6 @@ public class EngineCompositeGenerator extends ChunkGenerator implements IrisAcce public void directWriteMCA(World w, int x, int z, DirectWorldWriter writer, MultiBurst burst) { - if(writer.getMCAFile(x, z).exists()) - { - return; - } - BurstExecutor e = burst.burst(1024); int mcaox = x << 5; int mcaoz = z << 5; @@ -327,7 +323,9 @@ public class EngineCompositeGenerator extends ChunkGenerator implements IrisAcce for(int j = 0; j < 32; j++) { int jj = j; - e.queue(() -> directWriteChunk(w, ii + mcaox, jj + mcaoz, writer)); + e.queue(() -> { + directWriteChunk(w, ii + mcaox, jj + mcaoz, writer); + }); } } diff --git a/src/main/java/com/volmit/iris/scaffold/engine/IrisAccess.java b/src/main/java/com/volmit/iris/scaffold/engine/IrisAccess.java index 1c3a01dc7..f0f694bce 100644 --- a/src/main/java/com/volmit/iris/scaffold/engine/IrisAccess.java +++ b/src/main/java/com/volmit/iris/scaffold/engine/IrisAccess.java @@ -2,6 +2,7 @@ package com.volmit.iris.scaffold.engine; import com.volmit.iris.manager.IrisDataManager; import com.volmit.iris.object.*; +import com.volmit.iris.pregen.DirectWorldWriter; import com.volmit.iris.scaffold.data.DataProvider; import com.volmit.iris.scaffold.parallel.MultiBurst; import com.volmit.iris.util.*; diff --git a/src/main/java/com/volmit/iris/scaffold/hunk/io/HunkRegionSlice.java b/src/main/java/com/volmit/iris/scaffold/hunk/io/HunkRegionSlice.java index 84741f60f..878e3c599 100644 --- a/src/main/java/com/volmit/iris/scaffold/hunk/io/HunkRegionSlice.java +++ b/src/main/java/com/volmit/iris/scaffold/hunk/io/HunkRegionSlice.java @@ -80,7 +80,7 @@ public class HunkRegionSlice public void save() { - BurstExecutor e = MultiBurst.burst.burst(save.size()); + BurstExecutor e = MultiBurst.burst.burst(); for(ChunkPosition i : save.copy()) { if(i == null) @@ -89,7 +89,16 @@ public class HunkRegionSlice } e.queue(() -> save(i.getX(), i.getZ())); - save.remove(i); + + try + { + save.remove(i); + } + + catch(Throwable ef) + { + + } } e.complete(); diff --git a/src/main/java/com/volmit/iris/scaffold/parallel/MultiBurst.java b/src/main/java/com/volmit/iris/scaffold/parallel/MultiBurst.java index 30973a2af..abe8a868b 100644 --- a/src/main/java/com/volmit/iris/scaffold/parallel/MultiBurst.java +++ b/src/main/java/com/volmit/iris/scaffold/parallel/MultiBurst.java @@ -83,4 +83,12 @@ public class MultiBurst service.execute(o); } } + + public void shutdownNow() { + service.shutdownNow().forEach(Runnable::run); + } + + public void shutdown() { + service.shutdown(); + } } diff --git a/src/main/java/com/volmit/iris/util/PregenJob.java b/src/main/java/com/volmit/iris/util/PregenJob.java index fa4a1101c..7291aefe6 100644 --- a/src/main/java/com/volmit/iris/util/PregenJob.java +++ b/src/main/java/com/volmit/iris/util/PregenJob.java @@ -4,7 +4,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.IrisSettings; import com.volmit.iris.manager.gui.PregenGui; import com.volmit.iris.scaffold.IrisWorlds; -import com.volmit.iris.scaffold.engine.DirectWorldWriter; +import com.volmit.iris.pregen.DirectWorldWriter; import com.volmit.iris.scaffold.engine.IrisAccess; import com.volmit.iris.scaffold.parallel.MultiBurst; import io.papermc.lib.PaperLib; diff --git a/src/main/java/net/querz/mca/MCAUtil.java b/src/main/java/net/querz/mca/MCAUtil.java index f5ddecc5a..8a607ef7a 100644 --- a/src/main/java/net/querz/mca/MCAUtil.java +++ b/src/main/java/net/querz/mca/MCAUtil.java @@ -110,6 +110,11 @@ public final class MCAUtil { * @throws IOException If something goes wrong during serialization. * */ public static int write(MCAFile mcaFile, File file, boolean changeLastUpdate) throws IOException { + if(mcaFile == null) + { + return 0; + } + File to = file; if (file.exists()) { to = File.createTempFile(to.getName(), null);