diff --git a/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java b/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java new file mode 100644 index 000000000..bd3f110f9 --- /dev/null +++ b/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java @@ -0,0 +1,299 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.core.gui; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.gui.components.Pregenerator; +import com.volmit.iris.core.pregenerator.IrisPregenerator; +import com.volmit.iris.core.pregenerator.PregenListener; +import com.volmit.iris.core.pregenerator.PregenTask; +import com.volmit.iris.core.pregenerator.PregeneratorMethod; +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.function.Consumer2; +import com.volmit.iris.util.math.M; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.scheduling.J; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.image.BufferedImage; +import java.util.concurrent.locks.ReentrantLock; + +public class PregeneratorJob implements PregenListener { + public static PregeneratorJob instance; + private static final Color COLOR_GENERATING = parseColor("#346beb"); + private static final Color COLOR_GENERATED = parseColor("#34eb93"); + private JFrame frame; + private final PregenTask task; + private boolean saving; + private final IrisPregenerator pregenerator; + private PregenRenderer renderer; + private String[] info; + + public PregeneratorJob(PregenTask task, PregeneratorMethod method) + { + instance = this; + saving = false; + info = new String[]{"Initializing..."}; + this.task = task; + this.pregenerator = new IrisPregenerator(task, method, this); + this.pregenerator.start(); + } + + public void draw(int x, int z, Color color) + { + try + { + if(renderer != null && frame != null && frame.isVisible()) + { + renderer.func.accept(new Position2(x, z), color); + } + } + + catch(Throwable ignored) + { + + } + } + + public void stop() + { + J.a(() -> { + pregenerator.close(); + close(); + instance = null; + }); + } + + public void close() + { + J.a(() -> { + try + { + frame.setVisible(false); + } + + catch(Throwable e) + { + + } + }); + } + + public void open() + { + J.a(() -> { + try + { + frame = new JFrame("Pregen View"); + renderer = new PregenRenderer(); + frame.addKeyListener(renderer); + renderer.l = new ReentrantLock(); + renderer.frame = frame; + renderer.job = this; + renderer.func = (c, b) -> + { + renderer.l.lock(); + renderer.order.add(() -> renderer.draw(c, b, renderer.bg)); + renderer.l.unlock(); + }; + frame.add(renderer); + frame.setSize(1000, 1000); + frame.setVisible(true); + } + + catch(Throwable e) + { + + } + }); + } + + @Override + public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) { + info = new String[] { + (paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)", + "Speed: " + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m", + Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)", + "Generation Method: " + method, + }; + } + + @Override + public void onChunkGenerating(int x, int z) { + draw(x, z, COLOR_GENERATING); + } + + @Override + public void onChunkGenerated(int x, int z) { + draw(x, z, COLOR_GENERATED); + } + + @Override + public void onRegionGenerated(int x, int z) { + + } + + @Override + public void onRegionGenerating(int x, int z) { + + } + + @Override + public void onRegionSkipped(int x, int z) { + + } + + @Override + public void onClose() { + close(); + instance = null; + } + + @Override + public void onSaving() { + saving = true; + } + + private Position2 getMax() { + return task.getCenter().add(task.getRadius(), task.getRadius()).bottomRightChunkOfRegion(); + } + + private Position2 getMin() { + return task.getCenter().add(-task.getRadius(), -task.getRadius()).topLeftChunkOfRegion(); + } + + private boolean paused() { + return pregenerator.paused(); + } + + private String[] getProgress() { + return info; + } + + public static class PregenRenderer extends JPanel implements KeyListener { + private PregeneratorJob job; + private static final long serialVersionUID = 2094606939770332040L; + private final KList order = new KList<>(); + private final int res = 512; + Graphics2D bg; + private ReentrantLock l; + private final BufferedImage image = new BufferedImage(res, res, BufferedImage.TYPE_INT_RGB); + private Consumer2 func; + private JFrame frame; + + public PregenRenderer() { + + } + + public void paint(int x, int z, Color c) { + func.accept(new Position2(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) { + Iris.reportError(e); + + } + } + + l.unlock(); + g.drawImage(image, 0, 0, getParent().getWidth(), getParent().getHeight(), (img, infoflags, x, y, width, height) -> 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(IrisSettings.get().getGui().isMaximumPregenGuiFPS() ? 4 : 250); + repaint(); + } + + private void draw(Position2 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); + } + + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_P) { + Pregenerator.pauseResume(); + } + } + + public void close() { + frame.setVisible(false); + } + } + + private static Color parseColor(String c) { + String v = (c.startsWith("#") ? c : "#" + c).trim(); + try { + return Color.decode(v); + } catch (Throwable e) { + Iris.reportError(e); + Iris.error("Error Parsing 'color', (" + c + ")"); + } + + return Color.RED; + } +}