implement headless for 1.21.4

This commit is contained in:
Julian Krings
2025-01-26 14:28:59 +01:00
parent e5d21fdf7e
commit 3ff87566f5
13 changed files with 994 additions and 22 deletions

View File

@@ -140,12 +140,16 @@ public class CommandDeveloper implements DecreeExecutor {
public void packBenchmark(
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
IrisDimension dimension,
@Param(description = "Radius in regions", defaultValue = "5")
int radius,
@Param(description = "Diameter in regions", defaultValue = "5")
int diameter,
@Param(description = "Headless", defaultValue = "true")
boolean headless,
@Param(description = "Open GUI while benchmarking", defaultValue = "false")
boolean gui
) {
new IrisPackBenchmarking(dimension, radius, gui);
int rb = diameter << 9;
Iris.info("Benchmarking pack " + dimension.getName() + " with diameter: " + rb + "(" + diameter + ")");
new IrisPackBenchmarking(dimension, diameter, headless, gui);
}
@Decree(description = "Upgrade to another Minecraft version")

View File

@@ -0,0 +1,22 @@
package com.volmit.iris.core.nms;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates;
import com.volmit.iris.util.parallel.MultiBurst;
import java.io.Closeable;
import java.util.concurrent.CompletableFuture;
public interface IHeadless extends Closeable {
int getLoadedChunks();
@ChunkCoordinates
boolean exists(int x, int z);
@RegionCoordinates
CompletableFuture<Void> generateRegion(MultiBurst burst, int x, int z, int maxConcurrent, PregenListener listener);
@ChunkCoordinates
void generateChunk(int x, int z);
}

View File

@@ -30,15 +30,12 @@ import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color;
public interface INMSBinding {
@@ -124,5 +121,9 @@ public interface INMSBinding {
return 441;
}
default IHeadless createHeadless(Engine engine) {
return null;
}
KList<String> getStructureKeys();
}

View File

@@ -0,0 +1,109 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2024 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.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.IHeadless;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.parallel.MultiBurst;
import java.io.IOException;
import java.util.concurrent.Semaphore;
public class HeadlessPregenMethod implements PregeneratorMethod {
private final Engine engine;
private final IHeadless headless;
private final Semaphore semaphore;
private final int max;
private final MultiBurst burst;
public HeadlessPregenMethod(Engine engine) {
this.max = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism());
this.engine = engine;
this.headless = INMS.get().createHeadless(engine);
burst = new MultiBurst("HeadlessPregen", 8);
this.semaphore = new Semaphore(max);
}
@Override
public void init() {
}
@Override
public void close() {
try {
semaphore.acquire(max);
} catch (InterruptedException ignored) {
}
try {
headless.close();
} catch (IOException e) {
Iris.error("Failed to close headless");
e.printStackTrace();
}
burst.close();
}
@Override
public void save() {
}
@Override
public boolean supportsRegions(int x, int z, PregenListener listener) {
return false;
}
@Override
public String getMethod(int x, int z) {
return "Headless";
}
@Override
public void generateRegion(int x, int z, PregenListener listener) {
}
@Override
public void generateChunk(int x, int z, PregenListener listener) {
try {
semaphore.acquire();
} catch (InterruptedException ignored) {
return;
}
burst.complete(() -> {
try {
listener.onChunkGenerating(x, z);
headless.generateChunk(x, z);
listener.onChunkGenerated(x, z);
} finally {
semaphore.release();
}
});
}
@Override
public Mantle getMantle() {
return engine.getMantle().getMantle();
}
}

View File

@@ -2,9 +2,17 @@ package com.volmit.iris.core.tools;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.methods.HeadlessPregenMethod;
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.IrisEngine;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisWorld;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.exceptions.IrisException;
@@ -33,13 +41,16 @@ public class IrisPackBenchmarking {
public static boolean benchmarkInProgress = false;
private final PrecisionStopwatch stopwatch = new PrecisionStopwatch();
private final IrisDimension dimension;
private final int radius;
private final int diameter;
private final boolean gui;
private final boolean headless;
private transient Engine engine;
public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) {
public IrisPackBenchmarking(IrisDimension dimension, int diameter, boolean headless, boolean gui) {
instance = this;
this.dimension = dimension;
this.radius = radius;
this.diameter = diameter;
this.headless = headless;
this.gui = gui;
runBenchmark();
}
@@ -55,7 +66,7 @@ public class IrisPackBenchmarking {
deleteDirectory(file.toPath());
}
createBenchmark();
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
while (!headless && !IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
J.sleep(1000);
Iris.debug("Iris PackBenchmark: Waiting...");
}
@@ -73,7 +84,6 @@ public class IrisPackBenchmarking {
public void finishedBenchmark(KList<Integer> cps) {
try {
String time = Form.duration(stopwatch.getMillis());
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
Iris.info("-----------------");
Iris.info("Results:");
Iris.info("- Total time: " + time);
@@ -114,12 +124,16 @@ public class IrisPackBenchmarking {
e.printStackTrace();
}
J.s(() -> {
var world = Bukkit.getWorld("benchmark");
if (world == null) return;
IrisToolbelt.evacuate(world);
Bukkit.unloadWorld(world, true);
});
if (headless) {
engine.close();
} else {
J.s(() -> {
var world = Bukkit.getWorld("benchmark");
if (world == null) return;
IrisToolbelt.evacuate(world);
Bukkit.unloadWorld(world, true);
});
}
stopwatch.end();
} catch (Exception e) {
@@ -130,13 +144,34 @@ public class IrisPackBenchmarking {
private void createBenchmark() {
try {
IrisToolbelt.createWorld()
if (headless) {
Iris.info("Using headless benchmark!");
IrisWorld world = IrisWorld.builder()
.name("benchmark")
.minHeight(dimension.getMinHeight())
.maxHeight(dimension.getMaxHeight())
.seed(1337)
.worldFolder(new File(Bukkit.getWorldContainer(), "benchmark"))
.environment(dimension.getEnvironment())
.build();
Iris.service(StudioSVC.class).installIntoWorld(
Iris.getSender(),
dimension.getLoadKey(),
world.worldFolder());
var data = IrisData.get(new File(world.worldFolder(), "iris/pack"));
var dim = data.getDimensionLoader().load(dimension.getLoadKey());
engine = new IrisEngine(new EngineTarget(world, dim, data), false);
return;
}
engine = IrisToolbelt.access(IrisToolbelt.createWorld()
.dimension(dimension.getLoadKey())
.name("benchmark")
.seed(1337)
.studio(false)
.benchmark(true)
.create();
.create())
.getEngine();
} catch (IrisException e) {
throw new RuntimeException(e);
}
@@ -146,9 +181,15 @@ public class IrisPackBenchmarking {
IrisToolbelt.pregenerate(PregenTask
.builder()
.gui(gui)
.width(radius)
.height(radius)
.build(), Bukkit.getWorld("benchmark")
.width(diameter)
.height(diameter)
.build(), headless ?
new HeadlessPregenMethod(engine) :
new HybridPregenMethod(
engine.getWorld().realWorld(),
IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())
),
engine
);
}

View File

@@ -304,6 +304,11 @@ public class IrisEngine implements Engine {
return generated.get();
}
@Override
public void addGenerated(int x, int z) {
generated.incrementAndGet();
}
@Override
public double getGeneratedPerSecond() {
if (perSecondLatch.flip()) {

View File

@@ -612,6 +612,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
int getGenerated();
@ChunkCoordinates
void addGenerated(int x, int z);
CompletableFuture<Long> getHash32();
default <T> IrisPosition lookForStreamResult(T find, ProceduralStream<T> stream, Function2<T, T, Boolean> matcher, long timeout) {

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IHeadless;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.collection.KList;
import lombok.*;
@@ -48,6 +49,7 @@ public class IrisWorld {
private long seed;
private World.Environment environment;
private World realWorld;
private IHeadless headless;
private int minHeight;
private int maxHeight;

View File

@@ -0,0 +1,48 @@
package com.volmit.iris.util.hunk.view;
import com.volmit.iris.Iris;
import com.volmit.iris.util.hunk.storage.ArrayHunk;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator;
public class SyncChunkDataHunkHolder extends ArrayHunk<BlockData> {
private static final BlockData AIR = Material.AIR.createBlockData();
private final ChunkGenerator.ChunkData chunk;
private final Thread mainThread = Thread.currentThread();
public SyncChunkDataHunkHolder(ChunkGenerator.ChunkData chunk) {
super(16, chunk.getMaxHeight() - chunk.getMinHeight(), 16);
this.chunk = chunk;
}
@Override
public void setRaw(int x, int y, int z, BlockData data) {
if (Thread.currentThread() != mainThread)
Iris.warn("SyncChunkDataHunkHolder is not on the main thread");
super.setRaw(x, y, z, data);
}
@Override
public BlockData getRaw(int x, int y, int z) {
if (Thread.currentThread() != mainThread)
Iris.warn("SyncChunkDataHunkHolder is not on the main thread");
BlockData b = super.getRaw(x, y, z);
return b != null ? b : AIR;
}
public void apply() {
for (int i = getHeight()-1; i >= 0; i--) {
for (int j = 0; j < getWidth(); j++) {
for (int k = 0; k < getDepth(); k++) {
BlockData b = super.getRaw(j, i, k);
if (b != null) {
chunk.setBlock(j, i + chunk.getMinHeight(), k, b);
}
}
}
}
}
}