Merge pull request #437 from VolmitSoftware/new-pregenerator

...
This commit is contained in:
Dan 2021-07-19 06:42:27 -04:00 committed by GitHub
commit 2c1d4cfc40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1324 additions and 26 deletions

View File

@ -19,8 +19,13 @@
package com.volmit.iris.core.command.world;
import com.volmit.iris.Iris;
import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.gui.components.Pregenerator;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
import com.volmit.iris.core.pregenerator.methods.PaperOrMedievalPregenMethod;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.plugin.MortarCommand;
import com.volmit.iris.util.plugin.VolmitSender;
import org.bukkit.Bukkit;
@ -69,17 +74,17 @@ public class CommandIrisPregen extends MortarCommand {
}
if (args[0].equalsIgnoreCase("stop") || args[0].equalsIgnoreCase("x")) {
if (Pregenerator.shutdownInstance()) {
if (PregeneratorJob.shutdownInstance()) {
sender.sendMessage("Stopped Pregen.");
} else {
sender.sendMessage("No Active Pregens.");
}
return true;
} else if (args[0].equalsIgnoreCase("pause") || args[0].equalsIgnoreCase("resume")) {
if (Pregenerator.getInstance() != null) {
Pregenerator.pauseResume();
if (PregeneratorJob.getInstance() != null) {
PregeneratorJob.pauseResume();
if (Pregenerator.isPaused()) {
if (PregeneratorJob.isPaused()) {
sender.sendMessage("Pregen Paused");
} else {
sender.sendMessage("Pregen Resumed");
@ -105,7 +110,12 @@ public class CommandIrisPregen extends MortarCommand {
}
}
try {
new Pregenerator(world, getVal(args[0]) * 2);
new PregeneratorJob(PregenTask
.builder()
.center(new Position2(0, 0))
.radius(((getVal(args[0])>>4)>>5) + 1)
.build(),
new HybridPregenMethod(world, Runtime.getRuntime().availableProcessors()));
} catch (NumberFormatException e) {
Iris.reportError(e);
sender.sendMessage("Invalid argument in command");
@ -131,7 +141,12 @@ public class CommandIrisPregen extends MortarCommand {
}
World world = Bukkit.getWorld(args[1]);
try {
new Pregenerator(world, getVal(args[0]) * 2);
new PregeneratorJob(PregenTask
.builder()
.center(new Position2(0, 0))
.radius(((getVal(args[0])>>4)>>5) + 1)
.build(),
new HybridPregenMethod(world, Runtime.getRuntime().availableProcessors()));
} catch (NumberFormatException e) {
Iris.reportError(e);
sender.sendMessage("Invalid argument in command");

View File

@ -0,0 +1,351 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.gui;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
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.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.math.Spiraler;
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;
private Position2 min;
private Position2 max;
public PregeneratorJob(PregenTask task, PregeneratorMethod method)
{
instance = this;
saving = false;
info = new String[]{"Initializing..."};
this.task = task;
this.pregenerator = new IrisPregenerator(task, method, this);
J.a(this.pregenerator::start);
max = new Position2(0, 0);
min = new Position2(0, 0);
KList<Runnable> draw = new KList<>();
task.iterateRegions((xx,zz) -> {
min.setX(Math.min(xx << 5, min.getX()));
min.setZ(Math.min(zz << 5, min.getZ()));
max.setX(Math.max((xx << 5) + 31, max.getX()));
max.setZ(Math.max((zz << 5) + 31, max.getZ()));
});
open();
}
public static boolean shutdownInstance() {
if(instance == null)
{
return false;
}
J.a(() -> instance.pregenerator.close());
return true;
}
public static PregeneratorJob getInstance() {
return instance;
}
public static void pauseResume() {
if(instance == null)
{
return;
}
if(isPaused())
{
instance.pregenerator.resume();
}
else
{
instance.pregenerator.pause();
}
}
public static boolean isPaused() {
if(instance == null)
{
return true;
}
return instance.paused();
}
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
{
J.sleep(3000);
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() {
}
private Position2 getMax() {
return max;
}
private Position2 getMin() {
return min;
}
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<Runnable> 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<Position2, Color> 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) {
PregeneratorJob.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;
}
}

View File

@ -0,0 +1,212 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
public class IrisPregenerator {
private final PregenTask task;
private final PregeneratorMethod generator;
private final PregenListener listener;
private final Looper ticker;
private final AtomicBoolean paused;
private final AtomicBoolean shutdown;
private final RollingSequence chunksPerSecond;
private final RollingSequence chunksPerMinute;
private final RollingSequence regionsPerMinute;
private final AtomicInteger generated;
private final AtomicInteger generatedLast;
private final AtomicInteger generatedLastMinute;
private final AtomicInteger totalChunks;
private final AtomicLong startTime;
private final ChronoLatch minuteLatch;
private final AtomicReference<String> currentGeneratorMethod;
public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener)
{
this.listener = listenify(listener);
this.shutdown = new AtomicBoolean(false);
this.paused = new AtomicBoolean(false);
this.task = task;
this.generator = generator;
currentGeneratorMethod = new AtomicReference<>("Void");
minuteLatch = new ChronoLatch(60000, false);
chunksPerSecond = new RollingSequence(10);
chunksPerMinute = new RollingSequence(10);
regionsPerMinute = new RollingSequence(10);
generated = new AtomicInteger(0);
generatedLast = new AtomicInteger(0);
generatedLastMinute = new AtomicInteger(0);
totalChunks = new AtomicInteger(0);
task.iterateRegions((_a, _b) -> totalChunks.addAndGet(1024));
startTime = new AtomicLong(M.ms());
ticker = new Looper() {
@Override
protected long loop() {
long eta = computeETA();
int secondGenerated = generated.get() - generatedLast.get();
generatedLast.set(generated.get());
chunksPerSecond.put(secondGenerated);
if(minuteLatch.flip())
{
int minuteGenerated = generated.get() - generatedLastMinute.get();
generatedLastMinute.set(generated.get());
chunksPerMinute.put(minuteGenerated);
regionsPerMinute.put((double)minuteGenerated / 1024D);
}
listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(),
regionsPerMinute.getAverage(),
(double)generated.get() / (double)totalChunks.get(),
generated.get(), totalChunks.get(),
totalChunks.get() - generated.get(),
eta, M.ms() - startTime.get(), currentGeneratorMethod.get());
return 1000;
}
};
}
private long computeETA() {
return (long) ((totalChunks.get() - generated.get()) *
((double) (M.ms() - startTime.get()) / (double) generated.get()));
}
public void close()
{
shutdown.set(true);
}
public void start()
{
init();
ticker.start();
task.iterateRegions(this::visitRegion);
shutdown();
}
private void init() {
generator.init();
}
private void shutdown() {
listener.onSaving();
generator.close();
ticker.interrupt();
listener.onClose();
}
private void visitRegion(int x, int z) {
while(paused.get() && !shutdown.get())
{
J.sleep(50);
}
if(shutdown.get())
{
listener.onRegionSkipped(x, z);
return;
}
listener.onRegionGenerating(x, z);
currentGeneratorMethod.set(generator.getMethod(x, z));
if(generator.supportsRegions(x, z))
{
generator.generateRegion(x, z, listener);
}
else
{
PregenTask.iterateRegion(x, z, (xx, zz) -> generator.generateChunk(xx, zz, listener));
}
listener.onRegionGenerated(x, z);
listener.onSaving();
generator.save();
}
public void pause()
{
paused.set(true);
}
public void resume()
{
paused.set(false);
}
private PregenListener listenify(PregenListener listener) {
return new PregenListener() {
@Override
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
listener.onTick( chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method);
}
@Override
public void onChunkGenerating(int x, int z) {
listener.onChunkGenerating(x, z);
}
@Override
public void onChunkGenerated(int x, int z) {
listener.onChunkGenerated(x, z);
generated.addAndGet(1);
}
@Override
public void onRegionGenerated(int x, int z) {
listener.onRegionGenerated(x, z);
}
@Override
public void onRegionGenerating(int x, int z) {
listener.onRegionGenerating(x, z);
}
@Override
public void onRegionSkipped(int x, int z) {
listener.onRegionSkipped(x, z);
}
@Override
public void onClose() {
listener.onClose();
}
@Override
public void onSaving() {
listener.onSaving();
}
};
}
public boolean paused() {
return paused.get();
}
}

View File

@ -16,7 +16,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.gui;
package com.volmit.iris.core.pregenerator;
public class PregeneratorGUI {
public interface PregenListener {
void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method);
void onChunkGenerating(int x, int z);
void onChunkGenerated(int x, int z);
void onRegionGenerated(int x, int z);
void onRegionGenerating(int x, int z);
void onRegionSkipped(int x, int z);
void onClose();
void onSaving();
}

View File

@ -0,0 +1,76 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.Spiraled;
import com.volmit.iris.util.math.Spiraler;
import lombok.Builder;
import lombok.Data;
import java.util.Comparator;
@Builder
@Data
public class PregenTask {
@Builder.Default
private Position2 center = new Position2(0,0);
@Builder.Default
private int radius = 1;
private static final KList<Position2> order = computeChunkOrder();
public void iterateRegions(Spiraled s)
{
new Spiraler(radius * 2, radius * 2, s)
.setOffset(center.getX(), center.getZ()).drain();
}
public static void iterateRegion(int xr, int zr, Spiraled s)
{
for(Position2 i : order)
{
s.on(i.getX() + (xr << 5), i.getZ() + (zr << 5));
}
}
public void iterateAllChunks(Spiraled s)
{
new Spiraler(radius * 2, radius * 2, (x, z) -> iterateRegion(x, z, s))
.setOffset(center.getX(), center.getZ()).drain();
}
private static KList<Position2> computeChunkOrder() {
Position2 center = new Position2(15, 15);
KList<Position2> p = new KList<>();
new Spiraler(33, 33, (x, z) -> {
int xx = x + 15;
int zz = z + 15;
if (xx < 0 || xx > 31 || zz < 0 || zz > 31) {
return;
}
p.add(new Position2(xx, zz));
}).drain();
p.sort(Comparator.comparing((i) -> i.distance(center)));
return p;
}
}

View File

@ -0,0 +1,73 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator;
/**
* Represents something that is capable of generating in chunks or regions, or both
*/
public interface PregeneratorMethod {
/**
* This is called before any generate methods are called. Setup your generator here
*/
void init();
/**
* This is called after the pregenerator is done. Save your work and stop threads
*/
void close();
/**
* This is called every X amount of chunks or regions. Save work,
* but no need to save all of it. At the end, close() will still be called.
*/
void save();
/**
* Return true if regions can be generated
* @return true if they can be
* @param x the x region
* @param z the z region
*/
boolean supportsRegions(int x, int z);
/**
* Return the name of the method being used
* @return the name
* @param x the x region
* @param z the z region
*/
String getMethod(int x, int z);
/**
* Called to generate a region. Execute sync, if multicore internally, wait
* for the task to complete
* @param x the x
* @param z the z
* @param listener signal chunks generating & generated. Parallel capable.
*/
void generateRegion(int x, int z, PregenListener listener);
/**
* Called to generate a chunk. You can go async so long as save will wait on the threads to finish
* @param x the x
* @param z the z
* @param listener
*/
void generateChunk(int x, int z, PregenListener listener);
}

View File

@ -0,0 +1,59 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
public class DummyPregenMethod implements PregeneratorMethod {
@Override
public void init() {
}
@Override
public void close() {
}
@Override
public String getMethod(int x, int z) {
return "Dummy";
}
@Override
public void save() {
}
@Override
public boolean supportsRegions(int x, int z) {
return false;
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
}
}

View File

@ -0,0 +1,70 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.engine.headless.HeadlessGenerator;
import com.volmit.iris.engine.headless.HeadlessWorld;
public class HeadlessPregenMethod implements PregeneratorMethod {
private final HeadlessWorld world;
private final HeadlessGenerator generator;
public HeadlessPregenMethod(HeadlessWorld world)
{
this.world = world;
this.generator = world.generate();
}
@Override
public void init() {
}
@Override
public void close() {
generator.close();
}
@Override
public void save() {
generator.save();
}
@Override
public boolean supportsRegions(int x, int z) {
return true;
}
@Override
public String getMethod(int x, int z) {
return "Headless";
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
generator.generateRegion(x, z, listener);
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,87 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.engine.IrisWorlds;
import com.volmit.iris.engine.headless.HeadlessWorld;
import org.bukkit.World;
import java.io.File;
public class HybridPregenMethod implements PregeneratorMethod {
private final PregeneratorMethod headless;
private final PregeneratorMethod inWorld;
private final World world;
public HybridPregenMethod(World world, int threads)
{
this.world = world;
headless = supportsHeadless(world)
? new HeadlessPregenMethod(HeadlessWorld.from(world)) : new DummyPregenMethod();
inWorld = new PaperOrMedievalPregenMethod(world, threads);
}
private boolean supportsHeadless(World world) {
return IrisWorlds.access(world) != null;
}
@Override
public String getMethod(int x, int z) {
return "Hybrid<" + ((supportsRegions(x, z) ? headless.getMethod(x, z) : inWorld.getMethod(x, z)) + ">");
}
@Override
public void init() {
headless.init();
inWorld.init();
}
@Override
public void close() {
headless.close();
inWorld.close();
}
@Override
public void save() {
headless.save();
inWorld.save();
}
@Override
public boolean supportsRegions(int x, int z) {
if (headless instanceof DummyPregenMethod) {
return false;
}
return !new File(world.getWorldFolder(), "region/r." + x + "." + z + ".mca").exists();
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
headless.generateRegion(x, z, listener);
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
inWorld.generateChunk(x, z, listener);
}
}

View File

@ -0,0 +1,108 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.scheduling.J;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.util.concurrent.CompletableFuture;
public class MedievalPregenMethod implements PregeneratorMethod {
private final World world;
private final KList<CompletableFuture<?>> futures;
public MedievalPregenMethod(World world)
{
this.world = world;
futures = new KList<>();
}
private void waitForChunks()
{
for(CompletableFuture<?> i : futures)
{
try {
i.get();
} catch (Throwable e) {
e.printStackTrace();
}
}
futures.clear();
}
private void unloadAndSaveAllChunks() {
waitForChunks();
J.s(() -> {
for(Chunk i : world.getLoadedChunks())
{
i.unload(true);
}
world.save();
});
}
@Override
public void init() {
unloadAndSaveAllChunks();
}
@Override
public void close() {
unloadAndSaveAllChunks();
}
@Override
public void save() {
unloadAndSaveAllChunks();
}
@Override
public boolean supportsRegions(int x, int z) {
return false;
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
throw new UnsupportedOperationException();
}
@Override
public String getMethod(int x, int z) {
return "Medieval";
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
if(futures.size() > 32)
{
waitForChunks();
}
listener.onChunkGenerating(x, z);
futures.add(J.sfut(() -> {
world.getChunkAt(x, z);
listener.onChunkGenerated(x, z);
}));
}
}

View File

@ -0,0 +1,125 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.engine.parallel.MultiBurst;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.scheduling.J;
import io.papermc.lib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.util.concurrent.CompletableFuture;
public class PaperAsyncPregenMethod implements PregeneratorMethod {
private final World world;
private final MultiBurst burst;
private final KList<CompletableFuture<?>> future;
public PaperAsyncPregenMethod(World world, int threads)
{
if(!PaperLib.isPaper())
{
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
}
this.world = world;
burst = new MultiBurst("Iris PaperAsync Pregenerator", 6, threads);
future = new KList<>(1024);
}
private void unloadAndSaveAllChunks() {
J.s(() -> {
for(Chunk i : world.getLoadedChunks())
{
i.unload(true);
}
world.save();
});
}
private void completeChunk(int x, int z, PregenListener listener) {
try {
PaperLib.getChunkAtAsync(world, x, z, true).get();
listener.onChunkGenerated(x, z);
} catch (Throwable e) {
e.printStackTrace();
}
}
private void waitForChunks()
{
for(CompletableFuture<?> i : future)
{
try {
i.get();
} catch (Throwable e) {
e.printStackTrace();
}
}
future.clear();
}
@Override
public void init() {
unloadAndSaveAllChunks();
}
@Override
public String getMethod(int x, int z) {
return "Async";
}
@Override
public void close() {
waitForChunks();
burst.shutdownAndAwait();
unloadAndSaveAllChunks();
}
@Override
public void save() {
waitForChunks();
unloadAndSaveAllChunks();
}
@Override
public boolean supportsRegions(int x, int z) {
return false;
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
throw new UnsupportedOperationException();
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
if(future.size() > 128)
{
waitForChunks();
}
listener.onChunkGenerating(x, z);
future.add(burst.complete(() -> completeChunk(x, z, listener)));
}
}

View File

@ -0,0 +1,68 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import io.papermc.lib.PaperLib;
import org.bukkit.World;
public class PaperOrMedievalPregenMethod implements PregeneratorMethod {
private final PregeneratorMethod method;
public PaperOrMedievalPregenMethod(World world, int threads)
{
method = PaperLib.isPaper() ? new PaperAsyncPregenMethod(world, threads) : new MedievalPregenMethod(world);
}
@Override
public void init() {
method.init();
}
@Override
public void close() {
method.close();
}
@Override
public void save() {
method.save();
}
@Override
public String getMethod(int x, int z) {
return method.getMethod(x, z);
}
@Override
public boolean supportsRegions(int x, int z) {
return false;
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
throw new UnsupportedOperationException();
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
method.generateChunk(x, z, listener);
}
}

View File

@ -313,15 +313,7 @@ public class NBTWorld {
if(mcaf == null)
{
File f = getRegionFile(x, z);
try {
mcaf = f.exists() ? MCAUtil.read(f) : new MCAFile(x, z);
} catch (IOException e) {
Iris.error("Failed to properly read MCA File " + f.getPath() + " Using a blank one.");
e.printStackTrace();
mcaf = new MCAFile(x, z);
}
mcaf = new MCAFile(x, z);
regionLock.lock();
loadedRegions.put(key, mcaf);
regionLock.unlock();

View File

@ -23,6 +23,8 @@ import com.volmit.iris.core.IrisDataManager;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.BiomeBaseInjector;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.engine.IrisEngineCompound;
import com.volmit.iris.engine.IrisWorlds;
import com.volmit.iris.engine.cache.Cache;
@ -460,17 +462,24 @@ public class EngineCompositeGenerator extends ChunkGenerator implements IrisAcce
@Override
public void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst) {
BurstExecutor e = burst.burst(1024);
int mcaox = x << 5;
int mcaoz = z << 5;
directWriteMCA(w, x, z, writer, burst, null);
}
for (int i = 0; i < 32; i++) {
int ii = i;
for (int j = 0; j < 32; j++) {
int jj = j;
e.queue(() -> directWriteChunk(w, ii + mcaox, jj + mcaoz, writer));
@Override
public void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst, PregenListener l) {
BurstExecutor e = burst.burst(1024);
PregenTask.iterateRegion(x, z, (ii, jj) -> e.queue(() -> {
if(l != null)
{
l.onChunkGenerating(ii, jj);
}
}
directWriteChunk(w, ii, jj, writer);
if(l != null)
{
l.onChunkGenerated(ii, jj);
}
}));
e.complete();
}

View File

@ -20,6 +20,7 @@ package com.volmit.iris.engine.framework;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisDataManager;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.data.DataProvider;
import com.volmit.iris.engine.data.mca.NBTWorld;
@ -47,6 +48,8 @@ public interface IrisAccess extends Hotloadable, DataProvider {
void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst);
void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst, PregenListener listener);
void directWriteChunk(IrisWorld w, int x, int z, NBTWorld writer);
int getGenerated();

View File

@ -18,6 +18,7 @@
package com.volmit.iris.engine.headless;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.engine.data.mca.NBTWorld;
import com.volmit.iris.engine.framework.EngineCompositeGenerator;
import com.volmit.iris.engine.parallel.MultiBurst;
@ -51,6 +52,11 @@ public class HeadlessGenerator {
generator.directWriteMCA(world.getWorld(), x, z, writer, burst);
}
public void generateRegion(int x, int z, PregenListener listener)
{
generator.directWriteMCA(world.getWorld(), x, z, writer, burst, listener);
}
public File generateRegionToFile(int x, int z)
{
generateRegionToFile(x, z);

View File

@ -20,7 +20,9 @@ package com.volmit.iris.engine.headless;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisDataManager;
import com.volmit.iris.engine.IrisWorlds;
import com.volmit.iris.engine.framework.EngineCompositeGenerator;
import com.volmit.iris.engine.framework.IrisAccess;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.common.IrisWorld;
import com.volmit.iris.util.plugin.VolmitSender;
@ -73,6 +75,10 @@ public class HeadlessWorld {
.createWorld();
}
public static HeadlessWorld from(World world) {
return new HeadlessWorld(world.getName(), IrisWorlds.access(world).getTarget().getDimension(), world.getSeed());
}
public static HeadlessWorld from(String name, String dimension, long seed)
{
return new HeadlessWorld(name, IrisDataManager.loadAnyDimension(dimension), seed);

View File

@ -43,6 +43,11 @@ public class Position2 {
this.z = z;
}
public String toString()
{
return "[" + x + "," + z + "]";
}
@Override
public int hashCode() {
final int prime = 31;
@ -52,6 +57,11 @@ public class Position2 {
return result;
}
public Position2 regionToChunk()
{
return new Position2(x << 5, z << 5);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
@ -66,4 +76,8 @@ public class Position2 {
public double distance(Position2 center) {
return Math.pow(center.getX() - x, 2) + Math.pow(center.getZ() - z, 2);
}
public Position2 add(int x, int z) {
return new Position2(this.x + x, this.z + z);
}
}

View File

@ -208,6 +208,15 @@ public class J {
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r);
}
public static CompletableFuture sfut(Runnable r) {
CompletableFuture f = new CompletableFuture();
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
r.run();
f.complete(null);
});
return f;
}
/**
* Queue a sync task
*