Merge branch 'dev' into feat/folia

# Conflicts:
#	core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java
#	core/src/main/java/com/volmit/iris/engine/framework/Engine.java
#	core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java
This commit is contained in:
Julian Krings
2025-09-06 18:37:29 +02:00
152 changed files with 3666 additions and 1089 deletions

View File

@@ -32,13 +32,12 @@ plugins {
java
`java-library`
alias(libs.plugins.shadow)
alias(libs.plugins.sentry)
alias(libs.plugins.download)
alias(libs.plugins.runPaper)
}
group = "com.volmit"
version = "3.7.0-1.20.1-1.21.7"
version = "3.7.2-1.20.1-1.21.8"
apply<ApiGenerator>()
@@ -64,7 +63,7 @@ val serverMinHeap = "2G"
val serverMaxHeap = "8G"
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
val color = "truecolor"
val errorReporting = false
val errorReporting = findProperty("errorReporting") as Boolean? ?: false
val nmsBindings = mapOf(
"v1_21_R5" to "1.21.7-R0.1-SNAPSHOT",
@@ -110,7 +109,7 @@ nmsBindings.forEach { key, value ->
pluginJars(tasks.jar.flatMap { it.archiveFile })
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))}
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
systemProperty("disable.watchdog", "")
systemProperty("disable.watchdog", "true")
systemProperty("net.kyori.ansi.colorLevel", color)
systemProperty("com.mojang.eula.agree", true)
systemProperty("iris.suppressReporting", !errorReporting)
@@ -176,12 +175,13 @@ tasks {
group = "io.sentry"
dependsOn("downloadCli")
doLast {
val url = "http://sentry.volmit.com:8080"
val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN")
val org = "volmit-software"
val org = "sentry"
val projectName = "iris"
exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version)
exec(cli, "releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto", "--ignore-missing")
exec(cli, "releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version)
exec(cli, "--url", url , "--auth-token", authToken, "releases", "new", "-o", org, "-p", projectName, version)
exec(cli, "--url", url , "--auth-token", authToken, "releases", "set-commits", "-o", org, "-p", projectName, version, "--auto", "--ignore-missing")
//exec(cli, "--url", url, "--auth-token", authToken, "releases", "finalize", "-o", org, "-p", projectName, version)
cli.delete()
}
}
@@ -298,4 +298,4 @@ fun registerCustomOutputTaskUnix(name: String, path: String) {
into(file(path))
rename { "Iris.jar" }
}
}
}

View File

@@ -28,6 +28,8 @@ plugins {
alias(libs.plugins.sentry)
alias(libs.plugins.slimjar)
alias(libs.plugins.grgit)
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.lombok)
}
val apiVersion = "1.19"
@@ -92,11 +94,23 @@ dependencies {
slim(libs.zip)
slim(libs.gson)
slim(libs.asm)
slim(libs.bsf)
slim(libs.rhino)
slim(libs.caffeine)
slim(libs.byteBuddy.core)
slim(libs.byteBuddy.agent)
slim(libs.dom4j)
slim(libs.jaxen)
// Script Engine
slim(libs.kotlin.stdlib)
slim(libs.kotlin.coroutines)
slim(libs.kotlin.scripting.common)
slim(libs.kotlin.scripting.jvm)
slim(libs.kotlin.scripting.jvm.host)
slim(libs.kotlin.scripting.dependencies.maven) {
constraints {
slim(libs.mavenCore)
}
}
}
java {
@@ -104,10 +118,11 @@ java {
}
sentry {
url = "http://sentry.volmit.com:8080/"
autoInstallation.enabled = false
includeSourceContext = true
org = "volmit-software"
org = "sentry"
projectName = "iris"
authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN")
}
@@ -123,6 +138,13 @@ slimJar {
relocate("net.kyori", "$lib.kyori")
relocate("org.bstats", "$lib.metrics")
relocate("io.sentry", "$lib.sentry")
relocate("org.apache.maven", "$lib.maven")
relocate("org.codehaus.plexus", "$lib.plexus")
relocate("org.eclipse.sisu", "$lib.sisu")
relocate("org.eclipse.aether", "$lib.aether")
relocate("com.google.inject", "$lib.guice")
relocate("org.dom4j", "$lib.dom4j")
relocate("org.jaxen", "$lib.jaxen")
}
tasks {

View File

@@ -100,6 +100,7 @@ public class Iris extends VolmitPlugin implements Listener {
public static FileWatcher configWatcher;
public static Platform platform;
private static VolmitSender sender;
private static Thread shutdownHook;
static {
try {
@@ -457,6 +458,7 @@ public class Iris extends VolmitPlugin implements Listener {
configWatcher = new FileWatcher(getDataFile("settings.json"));
services.values().forEach(IrisService::onEnable);
services.values().forEach(this::registerListener);
addShutdownHook();
J.s(() -> {
//J.a(IrisSafeguard::suggestPaper); //TODO reimplement this
J.a(() -> IO.delete(getTemp()));
@@ -475,6 +477,24 @@ public class Iris extends VolmitPlugin implements Listener {
});
}
public void addShutdownHook() {
if (shutdownHook != null) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
shutdownHook = new Thread(() -> {
Bukkit.getWorlds()
.stream()
.map(IrisToolbelt::access)
.filter(Objects::nonNull)
.forEach(PlatformChunkGenerator::close);
MultiBurst.burst.close();
MultiBurst.ioBurst.close();
services.clear();
});
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
public void checkForBukkitWorlds(Predicate<String> filter) {
try {
IrisWorlds.readBukkitWorlds().forEach((s, generator) -> {
@@ -555,17 +575,7 @@ public class Iris extends VolmitPlugin implements Listener {
postShutdown.forEach(Runnable::run);
super.onDisable();
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
Bukkit.getWorlds()
.stream()
.map(IrisToolbelt::access)
.filter(Objects::nonNull)
.forEach(PlatformChunkGenerator::close);
MultiBurst.burst.close();
services.clear();
}));
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scanAll);
}
private void setupPapi() {
@@ -740,7 +750,7 @@ public class Iris extends VolmitPlugin implements Listener {
String padd2 = Form.repeat(" ", 4);
String[] info = {"", "", "", "", "", padd2 + C.IRIS + " Iris", padd2 + C.GRAY + " by " + "<rainbow>Volmit Software", padd2 + C.GRAY + " v" + C.IRIS + getDescription().getVersion()};
if (unstablemode) {
info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()};
info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()};
}
if (warningmode) {
info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()};

View File

@@ -49,11 +49,10 @@ public class IrisSettings {
private IrisSettingsSentry sentry = new IrisSettingsSentry();
public static int getThreadCount(int c) {
return switch (c) {
return Math.max(switch (c) {
case -1, -2, -4 -> Runtime.getRuntime().availableProcessors() / -c;
case 0, 1, 2 -> 1;
default -> Math.max(c, 2);
};
}, 1);
}
public static IrisSettings get() {
@@ -138,6 +137,7 @@ public class IrisSettings {
@Data
public static class IrisSettingsConcurrency {
public int parallelism = -1;
public int ioParallelism = -2;
public int worldGenParallelism = -1;
public int getWorldGenThreads() {
@@ -176,13 +176,13 @@ public class IrisSettings {
@Data
public static class IrisSettingsUpdater {
public double threadMultiplier = 2;
public int maxConcurrency = 256;
public double chunkLoadSensitivity = 0.7;
public MsRange emptyMsRange = new MsRange(80, 100);
public MsRange defaultMsRange = new MsRange(20, 40);
public double getThreadMultiplier() {
return Math.min(Math.abs(threadMultiplier), 0.1);
public int getMaxConcurrency() {
return Math.max(Math.abs(maxConcurrency), 1);
}
public double getChunkLoadSensitivity() {
@@ -242,6 +242,8 @@ public class IrisSettings {
public String defaultWorldType = "overworld";
public int maxBiomeChildDepth = 4;
public boolean preventLeafDecay = true;
public boolean useMulticore = false;
public boolean offsetNoiseTypes = false;
}
@Data
@@ -255,6 +257,7 @@ public class IrisSettings {
@Data
public static class IrisSettingsEngineSVC {
public boolean useVirtualThreads = true;
public boolean forceMulticoreWrite = false;
public int priority = Thread.NORM_PRIORITY;
public int getPriority() {

View File

@@ -37,6 +37,7 @@ import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.MultiBurst;
@@ -53,6 +54,7 @@ import org.bukkit.World;
import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -64,7 +66,6 @@ import java.util.zip.GZIPOutputStream;
public class CommandDeveloper implements DecreeExecutor {
private CommandTurboPregen turboPregen;
private CommandLazyPregen lazyPregen;
private CommandUpdater updater;
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
public void EngineStatus() {
@@ -79,6 +80,33 @@ public class CommandDeveloper implements DecreeExecutor {
Iris.reportError(new Exception("This is a test"));
}
@Decree(description = "Test")
public void mantle(@Param(defaultValue = "false") boolean plate, @Param(defaultValue = "21474836474") String name) throws Throwable {
var base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
var section = Iris.instance.getDataFile("dump", "pv." + name + ".section.bin");
//extractSection(base, section, 5604930, 4397);
if (plate) {
try (var in = CountingDataInputStream.wrap(new BufferedInputStream(new FileInputStream(base)))) {
new TectonicPlate(1088, in, true);
} catch (Throwable e) {
e.printStackTrace();
}
} else Matter.read(section);
if (!TectonicPlate.hasError())
Iris.info("Read " + (plate ? base : section).length() + " bytes from " + (plate ? base : section).getAbsolutePath());
}
private void extractSection(File source, File target, long offset, int length) throws IOException {
var raf = new RandomAccessFile(source, "r");
var bytes = new byte[length];
raf.seek(offset);
raf.readFully(bytes);
raf.close();
Files.write(target.toPath(), bytes);
}
@Decree(description = "Test")
public void dumpThreads() {
try {
@@ -115,27 +143,6 @@ public class CommandDeveloper implements DecreeExecutor {
}
}
@Decree(description = "Test")
public void benchmarkMantle(
@Param(description = "The world to bench", aliases = {"world"})
World world
) throws IOException, ClassNotFoundException {
Engine engine = IrisToolbelt.access(world).getEngine();
int maxHeight = engine.getTarget().getHeight();
File folder = new File(Bukkit.getWorldContainer(), world.getName());
int c = 0;
//MCAUtil.read()
File tectonicplates = new File(folder, "mantle");
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
TectonicPlate.read(maxHeight, i, true);
c++;
Iris.info("Loaded count: " + c );
}
}
@Decree(description = "Test")
public void packBenchmark(
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")

View File

@@ -21,7 +21,6 @@ package com.volmit.iris.core.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
@@ -33,7 +32,6 @@ import com.volmit.iris.util.decree.annotations.Decree;
import com.volmit.iris.util.decree.annotations.Param;
import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.misc.ServerProperties;
import com.volmit.iris.util.plugin.VolmitSender;
@@ -55,6 +53,7 @@ import static org.bukkit.Bukkit.getServer;
@Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command")
public class CommandIris implements DecreeExecutor {
private CommandUpdater updater;
private CommandStudio studio;
private CommandPregen pregen;
private CommandSettings settings;
@@ -312,24 +311,6 @@ public class CommandIris implements DecreeExecutor {
return dir.delete();
}
@Decree(description = "Updates all chunk in the specified world")
public void updater(
@Param(description = "World to update chunks at")
World world
) {
if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.GOLD + "This is not an Iris world");
return;
}
ChunkUpdater updater = new ChunkUpdater(world);
if (sender().isPlayer()) {
sender().sendMessage(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks()));
} else {
Iris.info(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks()));
}
updater.start();
}
@Decree(description = "Set aura spins")
public void aura(
@Param(description = "The h color value", defaultValue = "-20")

View File

@@ -47,7 +47,6 @@ import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
@@ -182,17 +181,32 @@ public class CommandStudio implements DecreeExecutor {
int rad = engine.getMantle().getRadius();
var mantle = engine.getMantle().getMantle();
var chunkMap = new KMap<Position2, MantleChunk>();
for (int i = -(radius + rad); i <= radius + rad; i++) {
for (int j = -(radius + rad); j <= radius + rad; j++) {
int xx = i + x, zz = j + z;
if (Math.abs(i) <= radius && Math.abs(j) <= radius) {
mantle.deleteChunk(xx, zz);
continue;
ParallelQueueJob<Position2> prep = new ParallelQueueJob<>() {
@Override
public void execute(Position2 pos) {
var cpos = pos.add(x, z);
if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) {
mantle.deleteChunk(cpos.getX(), cpos.getZ());
return;
}
chunkMap.put(new Position2(xx, zz), mantle.getChunk(xx, zz));
mantle.deleteChunk(xx, zz);
chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ()));
mantle.deleteChunk(cpos.getX(), cpos.getZ());
}
@Override
public String getName() {
return "Preparing Mantle";
}
};
for (int xx = -(radius + rad); xx <= radius + rad; xx++) {
for (int zz = -(radius + rad); zz <= radius + rad; zz++) {
prep.queue(new Position2(xx, zz));
}
}
CountDownLatch pLatch = new CountDownLatch(1);
prep.execute(sender(), pLatch::countDown);
pLatch.await();
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() {
@Override
@@ -210,23 +224,24 @@ public class CommandStudio implements DecreeExecutor {
job.queue(new Position2(i + x, j + z));
}
}
CountDownLatch latch = new CountDownLatch(1);
job.execute(sender(), latch::countDown);
latch.await();
int sections = mantle.getWorldHeight() >> 4;
chunkMap.forEach((pos, chunk) -> {
var c = mantle.getChunk(pos.getX(), pos.getZ());
for (MantleFlag flag : MantleFlag.values()) {
c.flag(flag, chunk.isFlagged(flag));
}
c.clear();
for (int y = 0; y < sections; y++) {
var slice = chunk.get(y);
if (slice == null) continue;
var s = c.getOrCreate(y);
slice.getSliceMap().forEach(s::putSlice);
var c = mantle.getChunk(pos.getX(), pos.getZ()).use();
try {
c.copyFlags(chunk);
c.clear();
for (int y = 0; y < sections; y++) {
var slice = chunk.get(y);
if (slice == null) continue;
var s = c.getOrCreate(y);
slice.getSliceMap().forEach(s::putSlice);
}
} finally {
c.release();
}
});
} catch (Throwable e) {
@@ -339,6 +354,42 @@ public class CommandStudio implements DecreeExecutor {
}, null);
}
@Decree(description = "Calculate the chance for each region to generate", origin = DecreeOrigin.PLAYER)
public void regions(@Param(description = "The radius in chunks", defaultValue = "500") int radius) {
var engine = engine();
if (engine == null) {
sender().sendMessage(C.RED + "Only works in an Iris world!");
return;
}
var sender = sender();
var player = player();
Thread.ofVirtual()
.start(() -> {
int d = radius * 2;
KMap<String, AtomicInteger> data = new KMap<>();
engine.getDimension().getRegions().forEach(key -> data.put(key, new AtomicInteger(0)));
var multiBurst = new MultiBurst("Region Sampler");
var executor = multiBurst.burst(radius * radius);
sender.sendMessage(C.GRAY + "Generating data...");
var loc = player.getLocation();
int totalTasks = d * d;
AtomicInteger completedTasks = new AtomicInteger(0);
int c = J.ar(() -> sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding regions"), 0);
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
var region = engine.getRegion((x << 4) + 8, (z << 4) + 8);
data.computeIfAbsent(region.getLoadKey(), (k) -> new AtomicInteger(0))
.incrementAndGet();
completedTasks.incrementAndGet();
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
executor.complete();
multiBurst.close();
J.car(c);
sender.sendMessage(C.GREEN + "Done!");
var loader = engine.getData().getRegionLoader();
data.forEach((k, v) -> sender.sendMessage(C.GREEN + k + ": " + loader.load(k).getRarity() + " / " + Form.f((double) v.get() / totalTasks * 100, 2) + "%"));
});
}
@Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER)
public void distances(@Param(description = "The radius in chunks") int radius) {
@@ -350,7 +401,7 @@ public class CommandStudio implements DecreeExecutor {
var sender = sender();
int d = radius * 2;
KMap<String, KList<Position2>> data = new KMap<>();
var multiBurst = new MultiBurst("Distance Sampler", Thread.MIN_PRIORITY);
var multiBurst = new MultiBurst("Distance Sampler");
var executor = multiBurst.burst(radius * radius);
sender.sendMessage(C.GRAY + "Generating data...");

View File

@@ -18,6 +18,7 @@
package com.volmit.iris.core.commands;
import lombok.Synchronized;
import org.bukkit.World;
import com.volmit.iris.Iris;
@@ -32,7 +33,8 @@ import com.volmit.iris.util.format.Form;
@Decree(name = "updater", origin = DecreeOrigin.BOTH, description = "Iris World Updater")
public class CommandUpdater implements DecreeExecutor {
private ChunkUpdater chunkUpdater;
private final Object lock = new Object();
private transient ChunkUpdater chunkUpdater;
@Decree(description = "Updates all chunk in the specified world")
public void start(
@@ -43,19 +45,22 @@ public class CommandUpdater implements DecreeExecutor {
sender().sendMessage(C.GOLD + "This is not an Iris world");
return;
}
if (chunkUpdater != null) {
chunkUpdater.stop();
}
synchronized (lock) {
if (chunkUpdater != null) {
chunkUpdater.stop();
}
chunkUpdater = new ChunkUpdater(world);
if (sender().isPlayer()) {
sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
} else {
Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
chunkUpdater = new ChunkUpdater(world);
if (sender().isPlayer()) {
sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
} else {
Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
}
chunkUpdater.start();
}
chunkUpdater.start();
}
@Synchronized("lock")
@Decree(description = "Pause the updater")
public void pause( ) {
if (chunkUpdater == null) {
@@ -78,6 +83,7 @@ public class CommandUpdater implements DecreeExecutor {
}
}
@Synchronized("lock")
@Decree(description = "Stops the updater")
public void stop() {
if (chunkUpdater == null) {

View File

@@ -30,7 +30,6 @@ 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.collection.KSet;
import com.volmit.iris.util.data.registry.Attributes;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.M;
@@ -40,7 +39,6 @@ import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.O;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
@@ -415,8 +413,7 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
}
private double getWorldX(double screenX) {
//return (mscale * screenX) + ((oxp / scale) * mscale);
return (mscale * screenX) + ((oxp / scale));
return (mscale * screenX) + ((oxp / scale) * mscale);
}
private double getWorldZ(double screenZ) {
@@ -486,6 +483,13 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
hz += Math.abs(hz - lz) * 0.36;
}
if (Math.abs(lx - hx) < 0.5) {
hx = lx;
}
if (Math.abs(lz - hz) < 0.5) {
hz = lz;
}
if (centities.flip()) {
J.s(() -> {
synchronized (lastEntities) {
@@ -510,8 +514,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
int iscale = (int) scale;
g.setColor(Color.white);
g.clearRect(0, 0, w, h);
int posX = (int) oxp;
int posZ = (int) ozp;
double offsetX = oxp / scale;
double offsetZ = ozp / scale;
m.set(3);
for (int r = 0; r < Math.max(w, h); r += iscale) {
@@ -520,10 +524,14 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
int a = i - (w / 2);
int b = j - (h / 2);
if (a * a + b * b <= r * r) {
BufferedImage t = getTile(gg, iscale, Math.floorDiv((posX / iscale) + i, iscale) * iscale, Math.floorDiv((posZ / iscale) + j, iscale) * iscale, m);
int tx = (int) (Math.floor((offsetX + i) / iscale) * iscale);
int tz = (int) (Math.floor((offsetZ + j) / iscale) * iscale);
BufferedImage t = getTile(gg, iscale, tx, tz, m);
if (t != null) {
g.drawImage(t, i - ((posX / iscale) % (iscale)), j - ((posZ / iscale) % (iscale)), iscale, iscale, (img, infoflags, x, y, width, height) -> true);
int rx = Math.floorMod((int) Math.floor(offsetX), iscale);
int rz = Math.floorMod((int) Math.floor(offsetZ), iscale);
g.drawImage(t, i - rx, j - rz, iscale, iscale, (img, infoflags, x, y, width, height) -> true);
}
}
}
@@ -651,8 +659,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
private void animateTo(double wx, double wz) {
double cx = getWorldX(getWidth() / 2);
double cz = getWorldZ(getHeight() / 2);
ox += (wx - cx);
oz += (wz - cz);
ox += ((wx - cx) / mscale) * scale;
oz += ((wz - cz) / mscale) * scale;
}
private void renderPosition(Graphics2D g, double x, double z) {
@@ -807,11 +815,28 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
return;
}
//Iris.info("Blocks/Pixel: " + (mscale) + ", Blocks Wide: " + (w * mscale));
double m0 = mscale;
double m1 = m0 + ((0.25 * m0) * notches);
m1 = Math.max(m1, 0.00001);
if (m1 == m0) {
return;
}
positions.clear();
fastpositions.clear();
mscale = mscale + ((0.25 * mscale) * notches);
mscale = Math.max(mscale, 0.00001);
Point p = e.getPoint();
double sx = p.getX();
double sz = p.getY();
double newOxp = scale * ((m0 / m1) * (sx + (oxp / scale)) - sx);
double newOzp = scale * ((m0 / m1) * (sz + (ozp / scale)) - sz);
mscale = m1;
oxp = newOxp;
ozp = newOzp;
ox = oxp;
oz = ozp;
}
@Override

View File

@@ -1,6 +1,8 @@
package com.volmit.iris.core.link;
import com.volmit.iris.core.link.data.DataType;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
@@ -22,7 +24,9 @@ import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.MissingResourceException;
@Getter
@@ -66,6 +70,18 @@ public abstract class ExternalDataProvider implements Listener {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
/**
* Retrieves a list of all {@link BlockProperty} objects associated with the specified block identifier.
*
* @param blockId The identifier of the block whose properties are to be retrieved. Must not be null.
* @return A list of {@link BlockProperty} objects representing the properties of the block.
* @throws MissingResourceException If the specified block identifier is invalid or cannot be found.
*/
@NotNull
public List<BlockProperty> getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException {
return List.of();
}
/**
* @see ExternalDataProvider#getItemStack(Identifier)
*/
@@ -137,4 +153,18 @@ public abstract class ExternalDataProvider implements Listener {
return new Pair<>(yaw, face);
}
protected static List<BlockProperty> YAW_FACE_BIOME_PROPERTIES = List.of(
BlockProperty.ofEnum(BiomeColor.class, "matchBiome", null),
BlockProperty.ofBoolean("randomYaw", false),
BlockProperty.ofFloat("yaw", 0, 0, 360f, false, true),
BlockProperty.ofBoolean("randomFace", true),
new BlockProperty(
"face",
BlockFace.class,
BlockFace.NORTH,
Arrays.asList(BlockFace.values()).subList(0, BlockFace.values().length - 1),
BlockFace::name
)
);
}

View File

@@ -4,24 +4,27 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.link.ExternalDataProvider;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.IrisCustomData;
import dev.lone.itemsadder.api.CustomBlock;
import dev.lone.itemsadder.api.CustomStack;
import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.EventHandler;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.MissingResourceException;
import java.util.stream.Collectors;
public class ItemAdderDataProvider extends ExternalDataProvider {
private KList<String> itemNamespaces, blockNamespaces;
private final KSet<String> itemNamespaces = new KSet<>();
private final KSet<String> blockNamespaces = new KSet<>();
public ItemAdderDataProvider() {
super("ItemsAdder");
@@ -29,16 +32,12 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
@Override
public void init() {
this.itemNamespaces = new KList<>();
this.blockNamespaces = new KList<>();
updateNamespaces();
}
for (Identifier i : getTypes(DataType.ITEM)) {
itemNamespaces.addIfMissing(i.namespace());
}
for (Identifier i : getTypes(DataType.BLOCK)) {
blockNamespaces.addIfMissing(i.namespace());
Iris.info("Found ItemAdder Block: " + i);
}
@EventHandler
public void onLoadData(ItemsAdderLoadDataEvent event) {
updateNamespaces();
}
@NotNull
@@ -48,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
if (block == null) {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
return new IrisCustomData(B.getAir(), blockId);
return new IrisCustomData(block.getBaseBlockData(), blockId);
}
@NotNull
@@ -81,9 +80,25 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
};
}
private void updateNamespaces() {
try {
updateNamespaces(DataType.ITEM);
updateNamespaces(DataType.BLOCK);
} catch (Throwable e) {
Iris.warn("Failed to update ItemAdder namespaces: " + e.getMessage());
}
}
private void updateNamespaces(DataType dataType) {
var namespaces = getTypes(dataType).stream().map(Identifier::namespace).collect(Collectors.toSet());
var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces;
currentNamespaces.removeIf(n -> !namespaces.contains(n));
currentNamespaces.addAll(namespaces);
}
@Override
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
if (dataType == DataType.ENTITY) return false;
return dataType == DataType.ITEM ? this.itemNamespaces.contains(id.namespace()) : this.blockNamespaces.contains(id.namespace());
return dataType == DataType.ITEM ? itemNamespaces.contains(id.namespace()) : blockNamespaces.contains(id.namespace());
}
}

View File

@@ -23,6 +23,7 @@ import com.volmit.iris.core.link.ExternalDataProvider;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
@@ -41,6 +42,7 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Optional;
@@ -77,6 +79,19 @@ public class MythicCrucibleDataProvider extends ExternalDataProvider {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
@Override
public @NotNull List<BlockProperty> getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException {
CrucibleItem crucibleItem = this.itemManager.getItem(blockId.key())
.orElseThrow(() -> new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()));
if (crucibleItem.getFurnitureData() != null) {
return YAW_FACE_BIOME_PROPERTIES;
} else if (crucibleItem.getBlockData() != null) {
return List.of();
}
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
@NotNull
@Override
public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) throws MissingResourceException {

View File

@@ -17,6 +17,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

View File

@@ -8,6 +8,7 @@ import com.volmit.iris.core.link.ExternalDataProvider;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
@@ -26,11 +27,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.MissingResourceException;
import java.util.concurrent.atomic.AtomicBoolean;
public class NexoDataProvider extends ExternalDataProvider {
private final AtomicBoolean failed = new AtomicBoolean(false);
public NexoDataProvider() {
super("Nexo");
}
@@ -59,6 +57,15 @@ public class NexoDataProvider extends ExternalDataProvider {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
@Override
public @NotNull List<BlockProperty> getBlockProperties(@NotNull Identifier blockId) throws MissingResourceException {
if (!NexoItems.exists(blockId.key())) {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
}
return NexoFurniture.isFurniture(blockId.key()) ? YAW_FACE_BIOME_PROPERTIES : List.of();
}
@NotNull
@Override
public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) throws MissingResourceException {
@@ -66,7 +73,12 @@ public class NexoDataProvider extends ExternalDataProvider {
if (builder == null) {
throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key());
}
return builder.build();
try {
return builder.build();
} catch (Exception e) {
e.printStackTrace();
throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key());
}
}
@Override
@@ -125,9 +137,4 @@ public class NexoDataProvider extends ExternalDataProvider {
if (dataType == DataType.ENTITY) return false;
return "nexo".equalsIgnoreCase(id.namespace());
}
@Override
public boolean isReady() {
return super.isReady() && !failed.get();
}
}

View File

@@ -24,6 +24,7 @@ import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.volmit.iris.Iris;
import com.volmit.iris.core.scripting.environment.PackEnvironment;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.*;
@@ -33,6 +34,8 @@ import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.mantle.flag.MantleFlagAdapter;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
@@ -54,6 +57,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
private static final KMap<File, IrisData> dataLoaders = new KMap<>();
private final File dataFolder;
private final int id;
private final PackEnvironment environment;
private boolean closed = false;
private ResourceLoader<IrisBiome> biomeLoader;
private ResourceLoader<IrisLootTable> lootLoader;
@@ -87,6 +91,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.engine = null;
this.dataFolder = dataFolder;
this.id = RNG.r.imax();
this.environment = PackEnvironment.create(this);
hotloaded();
}
@@ -252,12 +257,20 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
}
if (engine != null && t.getPreprocessors().isNotEmpty()) {
if (engine == null) return;
var global = engine.getDimension().getPreProcessors(t.getFolderName());
var local = t.getPreprocessors();
if ((global != null && global.isNotEmpty()) || local.isNotEmpty()) {
synchronized (this) {
engine.getExecution().getAPI().setPreprocessorObject(t);
if (global != null) {
for (String i : global) {
engine.getExecution().preprocessObject(i, t);
Iris.debug("Loader<" + C.GREEN + t.getTypeName() + C.LIGHT_PURPLE + "> iprocess " + C.YELLOW + t.getLoadKey() + C.LIGHT_PURPLE + " in <rainbow>" + i);
}
}
for (String i : t.getPreprocessors()) {
engine.getExecution().execute(i);
for (String i : local) {
engine.getExecution().preprocessObject(i, t);
Iris.debug("Loader<" + C.GREEN + t.getTypeName() + C.LIGHT_PURPLE + "> iprocess " + C.YELLOW + t.getLoadKey() + C.LIGHT_PURPLE + " in <rainbow>" + i);
}
}
@@ -271,6 +284,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
public void close() {
closed = true;
dump();
dataLoaders.remove(dataFolder);
}
public IrisData copy() {
@@ -311,12 +325,14 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
public synchronized void hotloaded() {
environment.close();
possibleSnippets = new KMap<>();
builder = new GsonBuilder()
.addDeserializationExclusionStrategy(this)
.addSerializationExclusionStrategy(this)
.setLenient()
.registerTypeAdapterFactory(this)
.registerTypeAdapter(MantleFlag.class, new MantleFlagAdapter())
.setPrettyPrinting();
loaders.clear();
File packs = dataFolder;
@@ -344,6 +360,10 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
gson = builder.create();
dimensionLoader.streamAll()
.map(IrisDimension::getDataScripts)
.flatMap(KList::stream)
.forEach(environment::execute);
}
public void dump() {
@@ -482,7 +502,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
public void savePrefetch(Engine engine) {
BurstExecutor b = MultiBurst.burst.burst(loaders.size());
BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size());
for (ResourceLoader<?> i : loaders.values()) {
b.queue(() -> {
@@ -499,7 +519,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
public void loadPrefetch(Engine engine) {
BurstExecutor b = MultiBurst.burst.burst(loaders.size());
BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size());
for (ResourceLoader<?> i : loaders.values()) {
b.queue(() -> {

View File

@@ -23,6 +23,7 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.project.SchemaBuilder;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.MeteredCache;
import com.volmit.iris.util.collection.KList;
@@ -44,10 +45,9 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.*;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -60,7 +60,7 @@ import java.util.zip.GZIPOutputStream;
public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
public static final AtomicDouble tlt = new AtomicDouble(0);
private static final int CACHE_SIZE = 100000;
protected final AtomicReference<KList<File>> folderCache;
protected final AtomicCache<KList<File>> folderCache;
protected KSet<String> firstAccess;
protected File root;
protected String folderName;
@@ -76,7 +76,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
public ResourceLoader(File root, IrisData manager, String folderName, String resourceTypeName, Class<? extends T> objectClass) {
this.manager = manager;
firstAccess = new KSet<>();
folderCache = new AtomicReference<>();
folderCache = new AtomicCache<>();
sec = new ChronoLatch(5000);
loads = new AtomicInteger();
this.objectClass = objectClass;
@@ -216,6 +216,10 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
return j;
}
public Stream<T> streamAll() {
return streamAll(Arrays.stream(getPossibleKeys()));
}
public Stream<T> streamAll(Stream<String> s) {
return s.map(this::load);
}
@@ -236,7 +240,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
public KList<T> loadAllParallel(KList<String> s) {
KList<T> m = new KList<>();
BurstExecutor burst = MultiBurst.burst.burst(s.size());
BurstExecutor burst = MultiBurst.ioBurst.burst(s.size());
for (String i : s) {
burst.queue(() -> {
@@ -361,29 +365,24 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
}
public KList<File> getFolders() {
synchronized (folderCache) {
if (folderCache.get() == null) {
KList<File> fc = new KList<>();
return folderCache.aquire(() -> {
KList<File> fc = new KList<>();
File[] files = root.listFiles();
if (files == null) {
throw new IllegalStateException("Failed to list files in " + root);
}
File[] files = root.listFiles();
if (files == null) {
throw new IllegalStateException("Failed to list files in " + root);
}
for (File i : files) {
if (i.isDirectory()) {
if (i.getName().equals(folderName)) {
fc.add(i);
break;
}
for (File i : files) {
if (i.isDirectory()) {
if (i.getName().equals(folderName)) {
fc.add(i);
break;
}
}
folderCache.set(fc);
}
}
return folderCache.get();
return fc;
});
}
public KList<File> getFolders(String rc) {
@@ -403,7 +402,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
public void clearCache() {
possibleKeys = null;
loadCache.invalidate();
folderCache.set(null);
folderCache.reset();
}
public File fileFor(T b) {
@@ -429,7 +428,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
}
public void clearList() {
folderCache.set(null);
folderCache.reset();
possibleKeys = null;
}

View File

@@ -82,8 +82,8 @@ public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
private Set<String> getKeysInDirectory(File directory) {
Set<String> keys = new HashSet<>();
for (File file : directory.listFiles()) {
if (file.isFile() && file.getName().endsWith(".js")) {
keys.add(file.getName().replaceAll("\\Q.js\\E", ""));
if (file.isFile() && file.getName().endsWith(".kts")) {
keys.add(file.getName().replaceAll("\\Q.kts\\E", ""));
} else if (file.isDirectory()) {
keys.addAll(getKeysInDirectory(file));
}
@@ -127,12 +127,12 @@ public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
public File findFile(String name) {
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".js") && j.getName().split("\\Q.\\E")[0].equals(name)) {
if (j.isFile() && j.getName().endsWith(".kts") && j.getName().split("\\Q.\\E")[0].equals(name)) {
return j;
}
}
File file = new File(i, name + ".js");
File file = new File(i, name + ".kts");
if (file.exists()) {
return file;
@@ -147,12 +147,12 @@ public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
private IrisScript loadRaw(String name) {
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".js") && j.getName().split("\\Q.\\E")[0].equals(name)) {
if (j.isFile() && j.getName().endsWith(".kts") && j.getName().split("\\Q.\\E")[0].equals(name)) {
return loadFile(j, name);
}
}
File file = new File(i, name + ".js");
File file = new File(i, name + ".kts");
if (file.exists()) {
return loadFile(file, name);

View File

@@ -20,6 +20,7 @@ package com.volmit.iris.core.nms;
import com.volmit.iris.core.link.FoliaWorldsLink;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
@@ -37,10 +38,10 @@ import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color;
import java.util.concurrent.CompletableFuture;
import java.util.List;
public interface INMSBinding {
boolean hasTile(Material material);
@@ -144,4 +145,6 @@ public interface INMSBinding {
default boolean injectBukkit() {
return true;
}
KMap<Material, List<BlockProperty>> getBlockProperties();
}

View File

@@ -0,0 +1,154 @@
package com.volmit.iris.core.nms.container;
import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Function;
public class BlockProperty {
private static final Set<Class<?>> NATIVES = Set.of(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, String.class);
private final String name;
private final Class<?> type;
private final Object defaultValue;
private final Set<Object> values;
private final Function<Object, String> nameFunction;
private final Function<Object, Object> jsonFunction;
public <T extends Comparable<T>> BlockProperty(
String name,
Class<T> type,
T defaultValue,
Collection<T> values,
Function<T, String> nameFunction
) {
this.name = name;
this.type = type;
this.defaultValue = defaultValue;
this.values = Collections.unmodifiableSet(new TreeSet<>(values));
this.nameFunction = (Function<Object, String>) (Object) nameFunction;
jsonFunction = NATIVES.contains(type) ? Function.identity() : this.nameFunction::apply;
}
public static <T extends Enum<T>> BlockProperty ofEnum(Class<T> type, String name, T defaultValue) {
return new BlockProperty(
name,
type,
defaultValue,
Arrays.asList(type.getEnumConstants()),
val -> val == null ? "null" : val.name()
);
}
public static BlockProperty ofFloat(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) {
return new BoundedDouble(
name,
defaultValue,
min,
max,
exclusiveMin,
exclusiveMax,
(f) -> String.format("%.2f", f)
);
}
public static BlockProperty ofBoolean(String name, boolean defaultValue) {
return new BlockProperty(
name,
Boolean.class,
defaultValue,
List.of(true, false),
(b) -> b ? "true" : "false"
);
}
@Override
public @NotNull String toString() {
return name + "=" + nameFunction.apply(defaultValue) + " [" + String.join(",", names()) + "]";
}
public String name() {
return name;
}
public String defaultValue() {
return nameFunction.apply(defaultValue);
}
public List<String> names() {
return values.stream().map(nameFunction).toList();
}
public Object defaultValueAsJson() {
return jsonFunction.apply(defaultValue);
}
public JSONArray valuesAsJson() {
return new JSONArray(values.stream().map(jsonFunction).toList());
}
public JSONObject buildJson() {
var json = new JSONObject();
json.put("type", jsonType());
json.put("default", defaultValueAsJson());
if (!values.isEmpty()) json.put("enum", valuesAsJson());
return json;
}
public String jsonType() {
if (type == Boolean.class)
return "boolean";
if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class)
return "integer";
if (type == Float.class || type == Double.class)
return "number";
return "string";
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (BlockProperty) obj;
return Objects.equals(this.name, that.name) &&
Objects.equals(this.values, that.values) &&
Objects.equals(this.type, that.type);
}
@Override
public int hashCode() {
return Objects.hash(name, values, type);
}
private static class BoundedDouble extends BlockProperty {
private final double min, max;
private final boolean exclusiveMin, exclusiveMax;
public BoundedDouble(
String name,
double defaultValue,
double min,
double max,
boolean exclusiveMin,
boolean exclusiveMax,
Function<Double, String> nameFunction
) {
super(name, Double.class, defaultValue, List.of(), nameFunction);
this.min = min;
this.max = max;
this.exclusiveMin = exclusiveMin;
this.exclusiveMax = exclusiveMax;
}
@Override
public JSONObject buildJson() {
return super.buildJson()
.put("minimum", min)
.put("maximum", max)
.put("exclusiveMinimum", exclusiveMin)
.put("exclusiveMaximum", exclusiveMax);
}
}
}

View File

@@ -21,6 +21,7 @@ package com.volmit.iris.core.nms.v1X;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
@@ -40,6 +41,7 @@ import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack;
import java.awt.Color;
import java.util.List;
import java.util.stream.StreamSupport;
public class NMSBinding1X implements INMSBinding {
@@ -124,6 +126,15 @@ public class NMSBinding1X implements INMSBinding {
return false;
}
@Override
public KMap<Material, List<BlockProperty>> getBlockProperties() {
KMap<Material, List<BlockProperty>> map = new KMap<>();
for (Material m : Material.values()) {
if (m.isBlock()) map.put(m, List.of());
}
return map;
}
@Override
public CompoundTag serializeEntity(Entity location) {
return null;

View File

@@ -8,7 +8,7 @@ import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RollingSequence;
@@ -38,14 +38,14 @@ public class ChunkUpdater {
private final AtomicInteger chunksUpdated = new AtomicInteger();
private final AtomicBoolean serverEmpty = new AtomicBoolean(true);
private final AtomicLong lastCpsTime = new AtomicLong(M.ms());
private final int coreLimit = (int) Math.max(Runtime.getRuntime().availableProcessors() * IrisSettings.get().getUpdater().getThreadMultiplier(), 1);
private final Semaphore semaphore = new Semaphore(256);
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, 256, IrisSettings.get().getUpdater().emptyMsRange);
private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency();
private final Semaphore semaphore = new Semaphore(maxConcurrency);
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange);
private final AtomicLong startTime = new AtomicLong();
private final Dimensions dimensions;
private final PregenTask task;
private final ExecutorService executor = Executors.newFixedThreadPool(coreLimit);
private final ExecutorService chunkExecutor = Executors.newFixedThreadPool(coreLimit);
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final CountDownLatch latch;
private final Engine engine;
@@ -137,10 +137,10 @@ public class ChunkUpdater {
loadBalancer.close();
semaphore.acquire(256);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
chunkExecutor.shutdown();
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
scheduler.shutdownNow();
unloadAndSaveAllChunks();
} catch (Exception ignored) {}

View File

@@ -3,7 +3,9 @@ package com.volmit.iris.core.pregenerator.cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.volmit.iris.Iris;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates;
@@ -14,12 +16,10 @@ import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import org.jetbrains.annotations.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import java.io.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@NotThreadSafe
@RequiredArgsConstructor
class PregenCacheImpl implements PregenCache {
private static final int SIZE = 32;
@@ -27,6 +27,8 @@ class PregenCacheImpl implements PregenCache {
private final HyperLock hyperLock = new HyperLock(SIZE * 2, true);
private final LoadingCache<Pos, Plate> cache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS)
.executor(KCache.EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.maximumSize(SIZE)
.removalListener(this::onRemoval)
.evictionListener(this::onRemoval)

View File

@@ -190,7 +190,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private class ServiceExecutor implements Executor {
private final ExecutorService service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
Executors.newVirtualThreadPerTaskExecutor() :
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
new MultiBurst("Iris Async Pregen");
public void generate(int x, int z, PregenListener listener) {
service.submit(() -> {

View File

@@ -0,0 +1,104 @@
package com.volmit.iris.core.project;
import com.volmit.iris.Iris;
import com.volmit.iris.util.io.IO;
import org.zeroturnaround.zip.ZipUtil;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Optional;
import java.util.Scanner;
public class Gradle {
private static final boolean WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
private static final String[] ENVIRONMENT = createEnvironment();
private static final String VERSION = "8.14.2";
private static final String DISTRIBUTION_URL = "https://services.gradle.org/distributions/gradle-" + VERSION + "-bin.zip";
private static final String HASH = IO.hash(DISTRIBUTION_URL);
public static synchronized void wrapper(File projectDir) {
try {
File settings = new File(projectDir, "settings.gradle.kts");
if (!settings.exists()) settings.createNewFile();
runGradle(projectDir, "wrapper");
} catch (Throwable e) {
Iris.error("Failed to install gradle wrapper!");
e.printStackTrace();
Iris.reportError(e);
}
}
public static void runGradle(File projectDir, String... args) throws IOException, InterruptedException {
File gradle = downloadGradle(false);
String[] cmd = new String[args.length + 1];
cmd[0] = gradle.getAbsolutePath();
System.arraycopy(args, 0, cmd, 1, args.length);
var process = Runtime.getRuntime().exec(cmd, ENVIRONMENT, projectDir);
attach(process.getInputStream());
attach(process.getErrorStream());
var code = process.waitFor();
if (code == 0) return;
throw new RuntimeException("Gradle exited with code " + code);
}
private static synchronized File downloadGradle(boolean force) {
var folder = Iris.instance.getDataFolder("cache", HASH.substring(0, 2), HASH);
if (force) {
IO.delete(folder);
folder.mkdirs();
}
var bin = new File(folder, "gradle-" + VERSION + "/bin/gradle" + (WINDOWS ? ".bat" : ""));
if (bin.exists()) {
bin.setExecutable(true);
return bin;
}
try (var input = new BufferedInputStream(URI.create(DISTRIBUTION_URL).toURL().openStream())) {
ZipUtil.unpack(input, folder);
} catch (Throwable e) {
throw new RuntimeException("Failed to download gradle", e);
}
bin.setExecutable(true);
return bin;
}
private static String[] createEnvironment() {
var env = new HashMap<>(System.getenv());
env.put("JAVA_HOME", findJavaHome());
return env.entrySet()
.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.toArray(String[]::new);
}
private static String findJavaHome() {
String javaHome = System.getProperty("java.home");
if (javaHome != null && new File(javaHome + "/bin/java" + (WINDOWS ? ".exe" : "")).exists()) {
return javaHome;
}
return ProcessHandle.current()
.info()
.command()
.map(s -> new File(s).getAbsoluteFile().getParentFile().getParentFile())
.flatMap(f -> f.exists() ? Optional.of(f.getAbsolutePath()) : Optional.empty())
.orElseThrow(() -> new RuntimeException("Failed to find java home, please set java.home system property"));
}
private static void attach(InputStream stream) {
Thread.ofVirtual().start(() -> {
try (var in = new Scanner(stream)) {
while (in.hasNextLine()) {
String line = in.nextLine();
Iris.debug("[GRADLE] " + line);
}
}
});
}
}

View File

@@ -24,6 +24,7 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.scripting.environment.SimpleEnvironment;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.object.annotations.Snippet;
@@ -49,6 +50,8 @@ import lombok.Data;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.World;
import org.dom4j.Document;
import org.dom4j.Element;
import org.zeroturnaround.zip.ZipUtil;
import java.awt.*;
@@ -217,24 +220,15 @@ public class IrisProject {
close();
}
boolean hasError = false;
if (hasError) {
return;
}
IrisDimension d = IrisData.loadAnyDimension(getName());
if (d == null) {
sender.sendMessage("Can't find dimension: " + getName());
return;
} else if (sender.isPlayer()) {
sender.player().setGameMode(GameMode.SPECTATOR);
}
openVSCode(sender);
J.a(() -> {
IrisDimension d = IrisData.loadAnyDimension(getName());
if (d == null) {
sender.sendMessage("Can't find dimension: " + getName());
return;
} else if (sender.isPlayer()) {
sender.player().setGameMode(GameMode.SPECTATOR);
}
try {
activeProvider = (PlatformChunkGenerator) IrisToolbelt.createWorld()
.seed(seed)
@@ -247,6 +241,8 @@ public class IrisProject {
} catch (IrisException e) {
e.printStackTrace();
}
openVSCode(sender);
});
}
@@ -359,6 +355,74 @@ public class IrisProject {
settings.put("json.schemas", schemas);
ws.put("settings", settings);
dm.getEnvironment().configureProject();
File schemasFile = new File(path, ".idea" + File.separator + "jsonSchemas.xml");
Document doc = IO.read(schemasFile);
Element mappings = (Element) doc.selectSingleNode("//component[@name='JsonSchemaMappingsProjectConfiguration']");
if (mappings == null) {
mappings = doc.getRootElement()
.addElement("component")
.addAttribute("name", "JsonSchemaMappingsProjectConfiguration");
}
Element state = (Element) mappings.selectSingleNode("state");
if (state == null) state = mappings.addElement("state");
Element map = (Element) state.selectSingleNode("map");
if (map == null) map = state.addElement("map");
var schemaMap = new KMap<String, String>();
schemas.forEach(element -> {
if (!(element instanceof JSONObject obj))
return;
String url = obj.getString("url");
String dir = obj.getJSONArray("fileMatch").getString(0);
schemaMap.put(url, dir.substring(1, dir.indexOf("/*")));
});
map.selectNodes("entry/value/SchemaInfo/option[@name='relativePathToSchema']")
.stream()
.map(node -> node.valueOf("@value"))
.forEach(schemaMap::remove);
var ideaSchemas = map;
schemaMap.forEach((url, dir) -> {
var genName = UUID.randomUUID().toString();
var info = ideaSchemas.addElement("entry")
.addAttribute("key", genName)
.addElement("value")
.addElement("SchemaInfo");
info.addElement("option")
.addAttribute("name", "generatedName")
.addAttribute("value", genName);
info.addElement("option")
.addAttribute("name", "name")
.addAttribute("value", dir);
info.addElement("option")
.addAttribute("name", "relativePathToSchema")
.addAttribute("value", url);
var item = info.addElement("option")
.addAttribute("name", "patterns")
.addElement("list")
.addElement("Item");
item.addElement("option")
.addAttribute("name", "directory")
.addAttribute("value", "true");
item.addElement("option")
.addAttribute("name", "path")
.addAttribute("value", dir);
item.addElement("option")
.addAttribute("name", "mappingKind")
.addAttribute("value", "Directory");
});
if (!schemaMap.isEmpty()) {
IO.write(schemasFile, doc);
}
Gradle.wrapper(path);
return ws;
}

View File

@@ -110,9 +110,12 @@ public class SchemaBuilder {
private JSONObject buildProperties(Class<?> c) {
JSONObject o = new JSONObject();
JSONObject properties = new JSONObject();
o.put("description", getDescription(c));
String desc = getDescription(c);
o.put("description", desc);
o.put("x-intellij-html-description", desc.replace("\n", "<br>"));
o.put("type", getType(c));
JSONArray required = new JSONArray();
JSONArray extended = new JSONArray();
if (c.isAssignableFrom(IrisRegistrant.class) || IrisRegistrant.class.isAssignableFrom(c)) {
for (Field k : IrisRegistrant.class.getDeclaredFields()) {
@@ -124,11 +127,15 @@ public class SchemaBuilder {
JSONObject property = buildProperty(k, c);
if (property.getBoolean("!required")) {
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
}
property.remove("!required");
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
}
@@ -142,15 +149,24 @@ public class SchemaBuilder {
JSONObject property = buildProperty(k, c);
if (property.getBoolean("!required"))
if (Boolean.TRUE == property.remove("!required")) {
required.put(k.getName());
property.remove("!required");
}
if (Boolean.TRUE == property.remove("!top")) {
extended.put(property);
continue;
}
properties.put(k.getName(), property);
}
if (required.length() > 0) {
o.put("required", required);
}
if (extended.length() > 0) {
o.put("allOf", extended);
}
o.put("properties", properties);
@@ -343,13 +359,63 @@ public class SchemaBuilder {
}
}
case "object" -> {
fancyType = k.getType().getSimpleName().replaceAll("\\QIris\\E", "") + " (Object)";
String key = "obj-" + k.getType().getCanonicalName().replaceAll("\\Q.\\E", "-").toLowerCase();
if (!definitions.containsKey(key)) {
definitions.put(key, new JSONObject());
definitions.put(key, buildProperties(k.getType()));
//TODO add back descriptions
if (k.isAnnotationPresent(RegistryMapBlockState.class)) {
String blockType = k.getDeclaredAnnotation(RegistryMapBlockState.class).value();
fancyType = "Block State";
prop.put("!top", true);
JSONArray any = new JSONArray();
prop.put("anyOf", any);
B.getBlockStates().forEach((blocks, properties) -> {
if (blocks.isEmpty()) return;
String raw = blocks.getFirst().replace(':', '_');
String enumKey = "enum-block-state-" + raw;
String propertiesKey = "obj-block-state-" + raw;
any.put(new JSONObject()
.put("if", new JSONObject()
.put("properties", new JSONObject()
.put(blockType, new JSONObject()
.put("type", "string")
.put("$ref", "#/definitions/" + enumKey))))
.put("then", new JSONObject()
.put("properties", new JSONObject()
.put(k.getName(), new JSONObject()
.put("type", "object")
.put("$ref", "#/definitions/" + propertiesKey))))
.put("else", false));
if (!definitions.containsKey(enumKey)) {
JSONArray filters = new JSONArray();
blocks.forEach(filters::put);
definitions.put(enumKey, new JSONObject()
.put("type", "string")
.put("enum", filters));
}
if (!definitions.containsKey(propertiesKey)) {
JSONObject props = new JSONObject();
properties.forEach(property -> {
props.put(property.name(), property.buildJson());
});
definitions.put(propertiesKey, new JSONObject()
.put("type", "object")
.put("properties", props));
}
});
} else {
fancyType = k.getType().getSimpleName().replaceAll("\\QIris\\E", "") + " (Object)";
String key = "obj-" + k.getType().getCanonicalName().replaceAll("\\Q.\\E", "-").toLowerCase();
if (!definitions.containsKey(key)) {
definitions.put(key, new JSONObject());
definitions.put(key, buildProperties(k.getType()));
}
prop.put("$ref", "#/definitions/" + key);
}
prop.put("$ref", "#/definitions/" + key);
}
case "array" -> {
fancyType = "List of Something...?";
@@ -520,11 +586,12 @@ public class SchemaBuilder {
}
KList<String> d = new KList<>();
d.add(k.getName());
d.add(getFieldDescription(k));
d.add(" ");
d.add(fancyType);
d.add(getDescription(k.getType()));
d.add("<h>" + k.getName() + "</h>");
d.add(getFieldDescription(k) + "<hr></hr>");
d.add("<h>" + fancyType + "</h>");
String typeDesc = getDescription(k.getType());
boolean present = !typeDesc.isBlank();
if (present) d.add(typeDesc);
Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class);
if (snippet == null) {
@@ -536,8 +603,9 @@ public class SchemaBuilder {
if (snippet != null) {
String sm = snippet.value();
d.add(" ");
if (present) d.add(" ");
d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here.");
present = false;
}
try {
@@ -545,15 +613,13 @@ public class SchemaBuilder {
Object value = k.get(cl.newInstance());
if (value != null) {
if (present) d.add(" ");
if (value instanceof List) {
d.add(" ");
d.add("* Default Value is an empty list");
d.add(SYMBOL_LIMIT__N + " Default Value is an empty list");
} else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !KeyedType.isKeyed(cl)) {
d.add(" ");
d.add("* Default Value is a default object (create this object to see default properties)");
d.add(SYMBOL_LIMIT__N + " Default Value is a default object (create this object to see default properties)");
} else {
d.add(" ");
d.add("* Default Value is " + value);
d.add(SYMBOL_LIMIT__N + " Default Value is " + value);
}
}
} catch (Throwable ignored) {
@@ -561,8 +627,14 @@ public class SchemaBuilder {
}
description.forEach((g) -> d.add(g.trim()));
String desc = d.toString("\n")
.replace("<hr></hr>", "\n")
.replace("<h>", "")
.replace("</h>", "");
String hDesc = d.toString("<br>");
prop.put("type", type);
prop.put("description", d.toString("\n"));
prop.put("description", desc);
prop.put("x-intellij-html-description", hDesc);
return buildSnippet(prop, k.getType());
}
@@ -588,8 +660,10 @@ public class SchemaBuilder {
arr.put(prop);
arr.put(str);
str.put("description", prop.getString("description"));
str.put("x-intellij-html-description", prop.getString("x-intellij-html-description"));
anyOf.put("anyOf", arr);
anyOf.put("description", prop.getString("description"));
anyOf.put("x-intellij-html-description", prop.getString("x-intellij-html-description"));
anyOf.put("!required", type.isAnnotationPresent(Required.class));
return anyOf;
@@ -615,7 +689,9 @@ public class SchemaBuilder {
String name = function.apply(gg);
j.put("const", name);
Desc dd = type.getField(name).getAnnotation(Desc.class);
j.put("description", dd == null ? ("No Description for " + name) : dd.value());
String desc = dd == null ? ("No Description for " + name) : dd.value();
j.put("description", desc);
j.put("x-intellij-html-description", desc.replace("\n", "<br>"));
a.put(j);
} catch (Throwable e) {
Iris.reportError(e);

View File

@@ -0,0 +1,25 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.scripting.kotlin.environment.IrisExecutionEnvironment;
import com.volmit.iris.engine.framework.Engine;
import lombok.NonNull;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
public interface EngineEnvironment extends PackEnvironment {
static EngineEnvironment create(@NonNull Engine engine) {
return new IrisExecutionEnvironment(engine);
}
@NonNull
Engine getEngine();
@Nullable
Object spawnMob(@NonNull String script, @NonNull Location location);
void postSpawnMob(@NonNull String script, @NonNull Location location, @NonNull Entity mob);
void preprocessObject(@NonNull String script, @NonNull IrisRegistrant object);
}

View File

@@ -0,0 +1,19 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.scripting.kotlin.environment.IrisPackExecutionEnvironment;
import com.volmit.iris.util.math.RNG;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;
public interface PackEnvironment extends SimpleEnvironment {
static PackEnvironment create(@NonNull IrisData data) {
return new IrisPackExecutionEnvironment(data);
}
@NonNull
IrisData getData();
@Nullable
Object createNoise(@NonNull String script, @NonNull RNG rng);
}

View File

@@ -0,0 +1,34 @@
package com.volmit.iris.core.scripting.environment;
import com.volmit.iris.core.scripting.kotlin.environment.IrisSimpleExecutionEnvironment;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Map;
public interface SimpleEnvironment {
static SimpleEnvironment create() {
return new IrisSimpleExecutionEnvironment();
}
static SimpleEnvironment create(@NonNull File projectDir) {
return new IrisSimpleExecutionEnvironment(projectDir);
}
void configureProject();
void execute(@NonNull String script);
void execute(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
@Nullable
Object evaluate(@NonNull String script);
@Nullable
Object evaluate(@NonNull String script, @NonNull Class<?> type, @Nullable Map<@NonNull String, Object> vars);
default void close() {
}
}

View File

@@ -0,0 +1,10 @@
package com.volmit.iris.core.scripting.func;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.util.documentation.BlockCoordinates;
@FunctionalInterface
public interface BiomeLookup {
@BlockCoordinates
IrisBiome at(int x, int z);
}

View File

@@ -21,6 +21,7 @@ package com.volmit.iris.core.service;
import com.volmit.iris.Iris;
import com.volmit.iris.core.link.*;
import com.volmit.iris.core.link.data.DataType;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.collection.KList;
@@ -69,8 +70,8 @@ public class ExternalDataSVC implements IrisService {
@EventHandler
public void onPluginEnable(PluginEnableEvent e) {
if (activeProviders.stream().noneMatch(p -> p.getPlugin().equals(e.getPlugin()))) {
providers.stream().filter(p -> p.isReady() && p.getPlugin().equals(e.getPlugin())).findFirst().ifPresent(edp -> {
if (activeProviders.stream().noneMatch(p -> e.getPlugin().equals(p.getPlugin()))) {
providers.stream().filter(p -> p.isReady() && e.getPlugin().equals(p.getPlugin())).findFirst().ifPresent(edp -> {
activeProviders.add(edp);
edp.init();
Iris.instance.registerListener(edp);
@@ -107,6 +108,18 @@ public class ExternalDataSVC implements IrisService {
}
}
public Optional<List<BlockProperty>> getBlockProperties(final Identifier key) {
Optional<ExternalDataProvider> provider = activeProviders.stream().filter(p -> p.isValidProvider(key, DataType.BLOCK)).findFirst();
if (provider.isEmpty())
return Optional.empty();
try {
return Optional.of(provider.get().getBlockProperties(key));
} catch (MissingResourceException e) {
Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]");
return Optional.empty();
}
}
public Optional<ItemStack> getItemStack(Identifier key, KMap<String, Object> customNbt) {
Optional<ExternalDataProvider> provider = activeProviders.stream().filter(p -> p.isValidProvider(key, DataType.ITEM)).findFirst();
if (provider.isEmpty()) {
@@ -150,6 +163,14 @@ public class ExternalDataSVC implements IrisService {
.toList();
}
public Collection<Pair<Identifier, List<BlockProperty>>> getAllBlockProperties() {
return activeProviders.stream()
.flatMap(p -> p.getTypes(DataType.BLOCK)
.stream()
.map(id -> new Pair<>(id, p.getBlockProperties(id))))
.toList();
}
public static Pair<Identifier, KMap<String, String>> parseState(Identifier key) {
if (!key.key().contains("[") || !key.key().contains("]")) {
return new Pair<>(key, new KMap<>());

View File

@@ -2,9 +2,11 @@ package com.volmit.iris.core.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.cache.PregenCache;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.plugin.IrisService;
import lombok.NonNull;
import org.bukkit.Bukkit;
@@ -20,7 +22,11 @@ import java.io.File;
import java.util.function.Function;
public class GlobalCacheSVC implements IrisService {
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build();
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder()
.executor(KCache.EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.weakValues()
.build();
private final KMap<String, PregenCache> globalCache = new KMap<>();
private transient boolean lastState;
private static boolean disabled = true;

View File

@@ -157,6 +157,7 @@ public class IrisEngineSVC implements IrisService {
private final class Registered {
private final String name;
private final PlatformChunkGenerator access;
private final int offset = RNG.r.nextInt(1000);
private transient ScheduledFuture<?> trimmer;
private transient ScheduledFuture<?> unloader;
private transient boolean closed;
@@ -193,7 +194,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to trim for " + name);
e.printStackTrace();
}
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
}, offset, 2000, TimeUnit.MILLISECONDS);
}
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
@@ -204,7 +205,7 @@ public class IrisEngineSVC implements IrisService {
try {
long unloadStart = System.currentTimeMillis();
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit());
int count = engine.getMantle().unloadTectonicPlate(IrisSettings.get().getPerformance().getEngineSVC().forceMulticoreWrite ? 0 : tectonicLimit());
if (count > 0) {
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
}
@@ -213,7 +214,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to unload for " + name);
e.printStackTrace();
}
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
}, offset + 1000, 2000, TimeUnit.MILLISECONDS);
}
}

View File

@@ -43,10 +43,6 @@ public class PreservationSVC implements IrisService {
threads.add(t);
}
public void register(MultiBurst burst) {
}
public void register(ExecutorService service) {
services.add(service);
}

View File

@@ -25,8 +25,6 @@ import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.DataProvider;
import com.volmit.iris.util.interpolation.IrisInterpolation.NoiseKey;
@@ -43,7 +41,7 @@ import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import java.io.File;
import java.util.UUID;
import java.util.*;
@Data
@EqualsAndHashCode(exclude = "data")
@@ -53,7 +51,7 @@ public class IrisComplex implements DataProvider {
private RNG rng;
private double fluidHeight;
private IrisData data;
private KMap<IrisInterpolator, KSet<IrisGenerator>> generators;
private Map<IrisInterpolator, Set<IrisGenerator>> generators;
private ProceduralStream<IrisRegion> regionStream;
private ProceduralStream<Double> regionStyleStream;
private ProceduralStream<Double> regionIdentityStream;
@@ -98,10 +96,10 @@ public class IrisComplex implements DataProvider {
this.data = engine.getData();
double height = engine.getMaxHeight();
fluidHeight = engine.getDimension().getFluidHeight();
generators = new KMap<>();
generators = new HashMap<>();
focusBiome = engine.getFocus();
focusRegion = engine.getFocusRegion();
KMap<InferredType, ProceduralStream<IrisBiome>> inferredStreams = new KMap<>();
Map<InferredType, ProceduralStream<IrisBiome>> inferredStreams = new HashMap<>();
if (focusBiome != null) {
focusBiome.setInferredType(InferredType.LAND);
@@ -118,6 +116,7 @@ public class IrisComplex implements DataProvider {
.getAllBiomes(this)
.forEach(this::registerGenerators));
}
boolean legacy = engine.getDimension().isLegacyRarity();
overlayStream = ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream");
engine.getDimension().getOverlayNoise().forEach(i -> overlayStream = overlayStream.add((x, z) -> i.get(rng, getData(), x, z)));
rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream()
@@ -131,7 +130,7 @@ public class IrisComplex implements DataProvider {
ProceduralStream.of((x, z) -> focusRegion,
Interpolated.of(a -> 0D, a -> focusRegion))
: regionStyleStream
.selectRarity(data.getRegionLoader().loadAll(engine.getDimension().getRegions()))
.selectRarity(data.getRegionLoader().loadAll(engine.getDimension().getRegions()), legacy)
.cache2D("regionStream", engine, cacheSize).waste("Region Stream");
regionIDStream = regionIdentityStream.convertCached((i) -> new UUID(Double.doubleToLongBits(i),
String.valueOf(i * 38445).hashCode() * 3245556666L)).waste("Region ID Stream");
@@ -140,7 +139,7 @@ public class IrisComplex implements DataProvider {
-> engine.getDimension().getCaveBiomeStyle().create(rng.nextParallelRNG(InferredType.CAVE.ordinal()), getData()).stream()
.zoom(engine.getDimension().getBiomeZoom())
.zoom(r.getCaveBiomeZoom())
.selectRarity(data.getBiomeLoader().loadAll(r.getCaveBiomes()))
.selectRarity(data.getBiomeLoader().loadAll(r.getCaveBiomes()), legacy)
.onNull(emptyBiome)
).convertAware2D(ProceduralStream::get).cache2D("caveBiomeStream", engine, cacheSize).waste("Cave Biome Stream");
inferredStreams.put(InferredType.CAVE, caveBiomeStream);
@@ -150,7 +149,7 @@ public class IrisComplex implements DataProvider {
.zoom(engine.getDimension().getBiomeZoom())
.zoom(engine.getDimension().getLandZoom())
.zoom(r.getLandBiomeZoom())
.selectRarity(data.getBiomeLoader().loadAll(r.getLandBiomes(), (t) -> t.setInferredType(InferredType.LAND)))
.selectRarity(data.getBiomeLoader().loadAll(r.getLandBiomes(), (t) -> t.setInferredType(InferredType.LAND)), legacy)
).convertAware2D(ProceduralStream::get)
.cache2D("landBiomeStream", engine, cacheSize).waste("Land Biome Stream");
inferredStreams.put(InferredType.LAND, landBiomeStream);
@@ -160,7 +159,7 @@ public class IrisComplex implements DataProvider {
.zoom(engine.getDimension().getBiomeZoom())
.zoom(engine.getDimension().getSeaZoom())
.zoom(r.getSeaBiomeZoom())
.selectRarity(data.getBiomeLoader().loadAll(r.getSeaBiomes(), (t) -> t.setInferredType(InferredType.SEA)))
.selectRarity(data.getBiomeLoader().loadAll(r.getSeaBiomes(), (t) -> t.setInferredType(InferredType.SEA)), legacy)
).convertAware2D(ProceduralStream::get)
.cache2D("seaBiomeStream", engine, cacheSize).waste("Sea Biome Stream");
inferredStreams.put(InferredType.SEA, seaBiomeStream);
@@ -169,7 +168,7 @@ public class IrisComplex implements DataProvider {
-> engine.getDimension().getShoreBiomeStyle().create(rng.nextParallelRNG(InferredType.SHORE.ordinal()), getData()).stream()
.zoom(engine.getDimension().getBiomeZoom())
.zoom(r.getShoreBiomeZoom())
.selectRarity(data.getBiomeLoader().loadAll(r.getShoreBiomes(), (t) -> t.setInferredType(InferredType.SHORE)))
.selectRarity(data.getBiomeLoader().loadAll(r.getShoreBiomes(), (t) -> t.setInferredType(InferredType.SHORE)), legacy)
).convertAware2D(ProceduralStream::get).cache2D("shoreBiomeStream", engine, cacheSize).waste("Shore Biome Stream");
inferredStreams.put(InferredType.SHORE, shoreBiomeStream);
bridgeStream = focusBiome != null ? ProceduralStream.of((x, z) -> focusBiome.getInferredType(),
@@ -302,12 +301,12 @@ public class IrisComplex implements DataProvider {
return biome;
}
private double interpolateGenerators(Engine engine, IrisInterpolator interpolator, KSet<IrisGenerator> generators, double x, double z, long seed) {
private double interpolateGenerators(Engine engine, IrisInterpolator interpolator, Set<IrisGenerator> generators, double x, double z, long seed) {
if (generators.isEmpty()) {
return 0;
}
KMap<NoiseKey, IrisBiome> cache = new KMap<>();
HashMap<NoiseKey, IrisBiome> cache = new HashMap<>(64);
double hi = interpolator.interpolate(x, z, (xx, zz) -> {
try {
IrisBiome bx = baseBiomeStream.get(xx, zz);
@@ -379,7 +378,7 @@ public class IrisComplex implements DataProvider {
}
private void registerGenerator(IrisGenerator cachedGenerator) {
generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new KSet<>()).add(cachedGenerator);
generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new HashSet<>()).add(cachedGenerator);
}
private IrisBiome implode(IrisBiome b, Double x, Double z) {

View File

@@ -28,12 +28,12 @@ import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.project.IrisProject;
import com.volmit.iris.core.scripting.environment.EngineEnvironment;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.framework.*;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.scripting.EngineExecutionEnvironment;
import com.volmit.iris.util.atomics.AtomicRollingSequence;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.ChunkContext;
@@ -43,7 +43,7 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterStructurePOI;
@@ -95,7 +95,7 @@ public class IrisEngine implements Engine {
private CompletableFuture<Long> hash32;
private EngineMode mode;
private EngineEffects effects;
private EngineExecutionEnvironment execution;
private EngineEnvironment execution;
private EngineWorldManager worldManager;
private volatile int parallelism;
private boolean failing;
@@ -171,10 +171,12 @@ public class IrisEngine implements Engine {
cacheId = RNG.r.nextInt();
worldManager = new IrisWorldManager(this);
complex = new IrisComplex(this);
execution = new IrisExecutionEnvironment(this);
execution = EngineEnvironment.create(this);
effects = new IrisEngineEffects(this);
hash32 = new CompletableFuture<>();
mantle.hotload();
setupMode();
getDimension().getEngineScripts().forEach(execution::execute);
J.a(this::computeBiomeMaxes);
J.a(() -> {
File[] roots = getData().getLoaders()
@@ -200,7 +202,7 @@ public class IrisEngine implements Engine {
mode.close();
}
mode = getDimension().getMode().getType().create(this);
mode = getDimension().getMode().create(this);
}
@Override

View File

@@ -29,13 +29,15 @@ import com.volmit.iris.engine.mantle.components.MantleJigsawComponent;
import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import lombok.*;
import java.io.File;
import java.util.stream.Collectors;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Data
@EqualsAndHashCode(exclude = "engine")
@@ -45,8 +47,9 @@ public class IrisEngineMantle implements EngineMantle {
private final Mantle mantle;
@Getter(AccessLevel.NONE)
private final KMap<Integer, KList<MantleComponent>> components;
private final AtomicCache<KList<Pair<KList<MantleComponent>, Integer>>> componentsCache = new AtomicCache<>();
private final AtomicCache<KSet<MantleFlag>> disabledFlags = new AtomicCache<>();
private final KMap<MantleFlag, MantleComponent> registeredComponents = new KMap<>();
private final AtomicCache<List<Pair<List<MantleComponent>, Integer>>> componentsCache = new AtomicCache<>();
private final AtomicCache<Set<MantleFlag>> disabledFlags = new AtomicCache<>();
private final MantleObjectComponent object;
private final MantleJigsawComponent jigsaw;
@@ -75,7 +78,7 @@ public class IrisEngineMantle implements EngineMantle {
}
@Override
public KList<Pair<KList<MantleComponent>, Integer>> getComponents() {
public List<Pair<List<MantleComponent>, Integer>> getComponents() {
return componentsCache.aquire(() -> {
var list = components.keySet()
.stream()
@@ -86,10 +89,9 @@ public class IrisEngineMantle implements EngineMantle {
.mapToInt(MantleComponent::getRadius)
.max()
.orElse(0);
return new Pair<>(components, radius);
return new Pair<>(List.copyOf(components), radius);
})
.collect(Collectors.toCollection(KList::new));
.toList();
int radius = 0;
for (var pair : list.reversed()) {
@@ -102,19 +104,36 @@ public class IrisEngineMantle implements EngineMantle {
}
@Override
public void registerComponent(MantleComponent c) {
c.setEnabled(!getDimension().getDisabledComponents().contains(c.getFlag()));
public Map<MantleFlag, MantleComponent> getRegisteredComponents() {
return Collections.unmodifiableMap(registeredComponents);
}
@Override
public boolean registerComponent(MantleComponent c) {
if (registeredComponents.putIfAbsent(c.getFlag(), c) != null) return false;
c.setEnabled(!getDisabledFlags().contains(c.getFlag()));
components.computeIfAbsent(c.getPriority(), k -> new KList<>()).add(c);
componentsCache.reset();
return true;
}
@Override
public KList<MantleFlag> getComponentFlags() {
return components.values()
.stream()
.flatMap(KList::stream)
.map(MantleComponent::getFlag)
.collect(KList.collector());
return new KList<>(registeredComponents.keySet());
}
@Override
public void hotload() {
disabledFlags.reset();
for (var component : registeredComponents.values()) {
component.hotload();
component.setEnabled(!getDisabledFlags().contains(component.getFlag()));
}
componentsCache.reset();
}
private Set<MantleFlag> getDisabledFlags() {
return disabledFlags.aquire(() -> Set.copyOf(getDimension().getDisabledComponents()));
}
@Override

View File

@@ -1,84 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.engine;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisScript;
import com.volmit.iris.engine.scripting.EngineExecutionEnvironment;
import com.volmit.iris.engine.scripting.IrisScriptingAPI;
import com.volmit.iris.util.format.C;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
import org.apache.bsf.engines.javascript.JavaScriptEngine;
@Data
@EqualsAndHashCode(exclude = "engine")
@ToString(exclude = "engine")
public class IrisExecutionEnvironment implements EngineExecutionEnvironment {
private final BSFManager manager;
private final Engine engine;
private final IrisScriptingAPI api;
private JavaScriptEngine javaScriptEngine;
public IrisExecutionEnvironment(Engine engine) {
this.engine = engine;
this.api = new IrisScriptingAPI(engine);
this.manager = new BSFManager();
this.manager.setClassLoader(Iris.class.getClassLoader());
try {
this.manager.declareBean("Iris", api, api.getClass());
this.javaScriptEngine = (JavaScriptEngine) this.manager.loadScriptingEngine("javascript");
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public IrisScriptingAPI getAPI() {
return api;
}
public void execute(String script) {
execute(getEngine().getData().getScriptLoader().load(script));
}
public void execute(IrisScript script) {
Iris.debug("Execute Script (void) " + C.DARK_GREEN + script.getLoadKey());
try {
javaScriptEngine.exec("", 0, 0, script);
} catch (BSFException e) {
e.printStackTrace();
}
}
public Object evaluate(String script) {
Iris.debug("Execute Script (for result) " + C.DARK_GREEN + script);
try {
return javaScriptEngine.eval("", 0, 0, getEngine().getData().getScriptLoader().load(script));
} catch (BSFException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -29,7 +29,7 @@ import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;

View File

@@ -29,13 +29,13 @@ import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.scripting.environment.EngineEnvironment;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.data.chunk.TerrainChunk;
import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.scripting.EngineExecutionEnvironment;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.ChunkContext;
@@ -48,7 +48,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.function.Function2;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
@@ -109,7 +109,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
IrisContext getContext();
EngineExecutionEnvironment getExecution();
EngineEnvironment getExecution();
double getMaxBiomeObjectDensity();
@@ -295,68 +295,63 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
try {
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
var region = Iris.platform.getRegionScheduler();
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> region.run(c.getWorld(), c.getX(), c.getZ(), () -> {
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
Block block = c.getBlock(x, y + getWorld().minHeight(), z);
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> {
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
if (!TileData.setTileState(block, v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey());
});
})));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> region.run(c.getWorld(), c.getX(), c.getZ(), () -> {
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
}, c, 1));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> {
chunk.iterate(Identifier.class, (x, y, z, v) -> {
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
});
})));
}, c, 1));
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> region.runDelayed(c.getWorld(), c.getX(), c.getZ(), () -> {
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> {
PrecisionStopwatch p = PrecisionStopwatch.start();
KMap<Long, Integer> updates = new KMap<>();
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
int[][] grid = new int[16][16];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
grid[x][z] = Integer.MIN_VALUE;
}
}
RNG rng = new RNG(Cache.key(c.getX(), c.getZ()));
chunk.iterate(MatterCavern.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
x &= 15;
z &= 15;
Block block = c.getBlock(x, y, z);
if (!B.isFluid(block.getBlockData())) {
return;
}
boolean u = false;
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
u = true;
}
boolean u = B.isAir(block.getRelative(BlockFace.DOWN).getBlockData())
|| B.isAir(block.getRelative(BlockFace.WEST).getBlockData())
|| B.isAir(block.getRelative(BlockFace.EAST).getBlockData())
|| B.isAir(block.getRelative(BlockFace.SOUTH).getBlockData())
|| B.isAir(block.getRelative(BlockFace.NORTH).getBlockData());
if (u) {
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
if (vv != null) {
return Math.max(vv, y);
}
return y;
});
}
if (u) grid[x][z] = Math.max(grid[x][z], y);
});
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (grid[x][z] == Integer.MIN_VALUE)
continue;
update(x, grid[x][z], z, c, rng);
}
}
chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) {
int vx = x & 15;
int vz = z & 15;
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
updateLighting(x, y, z, c);
}
update(x, y, z, c, rng);
}
});
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
chunk.deleteSlices(MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds());
}, RNG.r.i(1, 20))));
}, c, RNG.r.i(2, 20))); //Why is there a random delay here?
});
try {
@@ -367,33 +362,21 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
}
private static Runnable run(Semaphore semaphore, Runnable runnable) {
private static Runnable run(Semaphore semaphore, Runnable runnable, Chunk chunk, int delay) {
return () -> {
if (!semaphore.tryAcquire())
return;
try {
runnable.run();
} finally {
semaphore.release();
}
Iris.platform.getRegionScheduler().runDelayed(chunk.getWorld(), chunk.getX(), chunk.getZ(), () -> {
try {
runnable.run();
} finally {
semaphore.release();
}
}, delay);
};
}
@BlockCoordinates
default void updateLighting(int x, int y, int z, Chunk c) {
Block block = c.getBlock(x, y, z);
BlockData data = block.getBlockData();
if (B.isLit(data)) {
try {
block.setType(Material.AIR, false);
block.setBlockData(data, true);
} catch (Exception e) {
Iris.reportError(e);
}
}
}
@BlockCoordinates
@Override

View File

@@ -22,8 +22,7 @@ import com.volmit.iris.engine.object.IrisObject;
import com.volmit.iris.engine.object.IrisObjectPlacement;
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Nullable;
@Data
@AllArgsConstructor

View File

@@ -1,6 +1,6 @@
package com.volmit.iris.engine.mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.ReservedFlag;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -10,5 +10,5 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentFlag {
MantleFlag value();
ReservedFlag value();
}

View File

@@ -26,31 +26,33 @@ import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget;
import com.volmit.iris.engine.mantle.components.MantleJigsawComponent;
import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
import com.volmit.iris.engine.object.IObjectPlacer;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisPosition;
import com.volmit.iris.engine.object.TileData;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.IrisCustomData;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.matter.*;
import com.volmit.iris.util.matter.slices.UpdateMatter;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
// TODO: MOVE PLACER OUT OF MATTER INTO ITS OWN THING
public interface EngineMantle extends IObjectPlacer {
import static com.volmit.iris.util.parallel.StreamUtils.forEach;
import static com.volmit.iris.util.parallel.StreamUtils.streamRadius;
public interface EngineMantle {
BlockData AIR = B.get("AIR");
Mantle getMantle();
@@ -61,12 +63,19 @@ public interface EngineMantle extends IObjectPlacer {
int getRealRadius();
KList<Pair<KList<MantleComponent>, Integer>> getComponents();
@UnmodifiableView
List<Pair<List<MantleComponent>, Integer>> getComponents();
void registerComponent(MantleComponent c);
@UnmodifiableView
Map<MantleFlag, MantleComponent> getRegisteredComponents();
boolean registerComponent(MantleComponent c);
@UnmodifiableView
KList<MantleFlag> getComponentFlags();
void hotload();
default int getHighest(int x, int z) {
return getHighest(x, z, getData());
}
@@ -87,12 +96,10 @@ public interface EngineMantle extends IObjectPlacer {
return getHighest(x, z, getData(), ignoreFluid);
}
@Override
default int getHighest(int x, int z, IrisData data) {
return getHighest(x, z, data, false);
}
@Override
default int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
return ignoreFluid ? trueHeight(x, z) : Math.max(trueHeight(x, z), getEngine().getDimension().getFluidHeight());
}
@@ -101,24 +108,12 @@ public interface EngineMantle extends IObjectPlacer {
return getComplex().getRoundedHeighteightStream().get(x, z);
}
@Deprecated(forRemoval = true)
default boolean isCarved(int x, int h, int z) {
return getMantle().get(x, h, z, MatterCavern.class) != null;
}
@Override
default void set(int x, int y, int z, BlockData d) {
if (d instanceof IrisCustomData data) {
getMantle().set(x, y, z, data.getBase());
getMantle().set(x, y, z, data.getCustom());
} else getMantle().set(x, y, z, d == null ? AIR : d);
}
@Override
default void setTile(int x, int y, int z, TileData d) {
getMantle().set(x, y, z, new TileWrapper(d));
}
@Override
@Deprecated(forRemoval = true)
default BlockData get(int x, int y, int z) {
BlockData block = getMantle().get(x, y, z, BlockData.class);
if (block == null)
@@ -126,27 +121,18 @@ public interface EngineMantle extends IObjectPlacer {
return block;
}
@Override
default boolean isPreventingDecay() {
return getEngine().getDimension().isPreventLeafDecay();
}
@Override
default boolean isSolid(int x, int y, int z) {
return B.isSolid(get(x, y, z));
}
@Override
default boolean isUnderwater(int x, int z) {
return getHighest(x, z, true) <= getFluidHeight();
}
@Override
default int getFluidHeight() {
return getEngine().getDimension().getFluidHeight();
}
@Override
default boolean isDebugSmartBore() {
return getEngine().getDimension().isDebugSmartBore();
}
@@ -196,7 +182,7 @@ public interface EngineMantle extends IObjectPlacer {
@ChunkCoordinates
default void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
if (!getEngine().getDimension().isUseMantle()) {
if (!getEngine().getDimension().isUseMantle() || getMantle().hasFlag(x, z, MantleFlag.PLANNED)) {
return;
}
@@ -206,30 +192,33 @@ public interface EngineMantle extends IObjectPlacer {
var pair = iterator.next();
int radius = pair.getB();
boolean last = !iterator.hasNext();
BurstExecutor burst = burst().burst(radius * 2 + 1);
burst.setMulticore(multicore);
forEach(streamRadius(x, z, radius),
pos -> pair.getA()
.stream()
.filter(MantleComponent::isEnabled)
.map(c -> new Pair<>(c, pos)),
p -> {
MantleComponent c = p.getA();
Position2 pos = p.getB();
int xx = pos.getX();
int zz = pos.getZ();
IrisContext.getOr(getEngine()).setChunkContext(context);
generateMantleComponent(writer, xx, zz, c, writer.acquireChunk(xx, zz), context);
},
multicore ? burst() : null
);
for (int i = -radius; i <= radius; i++) {
for (int j = -radius; j <= radius; j++) {
int xx = x + i;
int zz = z + j;
MantleChunk mc = getMantle().getChunk(xx, zz);
burst.queue(() -> {
IrisContext.touch(getEngine().getContext());
pair.getA().forEach(k -> generateMantleComponent(writer, xx, zz, k, mc, context));
if (last) mc.flag(MantleFlag.PLANNED, true);
});
}
}
burst.complete();
if (!last) continue;
forEach(streamRadius(x, z, radius),
p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true),
multicore ? burst() : null
);
}
}
}
default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) {
mc.raiseFlag(c.getFlag(), () -> {
mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> {
if (c.isEnabled()) c.generateLayer(writer, x, z, context);
});
}
@@ -240,7 +229,12 @@ public interface EngineMantle extends IObjectPlacer {
return;
}
getMantle().iterateChunk(x, z, t, blocks::set);
var chunk = getMantle().getChunk(x, z).use();
try {
chunk.iterate(t, blocks::set);
} finally {
chunk.release();
}
}
@BlockCoordinates

View File

@@ -18,7 +18,7 @@
package com.volmit.iris.engine.mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@@ -30,5 +30,32 @@ public abstract class IrisMantleComponent implements MantleComponent {
private final EngineMantle engineMantle;
private final MantleFlag flag;
private final int priority;
private volatile int radius = -1;
private final Object lock = new Object();
private boolean enabled = true;
protected abstract int computeRadius();
@Override
public void hotload() {
synchronized (lock) {
radius = -1;
}
}
@Override
public final int getRadius() {
int r = radius;
if(r != -1) return r;
synchronized (lock) {
if((r = radius) != -1) {
return r;
}
r = computeRadius();
if(r < 0) r = 0;
return radius = r;
}
}
}

View File

@@ -24,7 +24,7 @@ import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.parallel.BurstExecutor;
import org.jetbrains.annotations.NotNull;
@@ -65,6 +65,8 @@ public interface MantleComponent extends Comparable<MantleComponent> {
void setEnabled(boolean b);
void hotload();
@ChunkCoordinates
void generateLayer(MantleWriter writer, int x, int z, ChunkContext context);

View File

@@ -29,12 +29,16 @@ import com.volmit.iris.engine.object.IrisPosition;
import com.volmit.iris.engine.object.TileData;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.IrisCustomData;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.function.Function3;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterCavern;
import com.volmit.iris.util.matter.TileWrapper;
import com.volmit.iris.util.noise.CNG;
import lombok.Data;
import org.bukkit.block.data.BlockData;
@@ -44,6 +48,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
@Data
public class MantleWriter implements IObjectPlacer, AutoCloseable {
private final EngineMantle engineMantle;
@@ -144,20 +150,41 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
return;
}
if (cx >= this.x - radius && cx <= this.x + radius
&& cz >= this.z - radius && cz <= this.z + radius) {
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
MantleChunk chunk = acquireChunk(cx, cz);
if (chunk == null) return;
if (chunk == null) {
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
return;
}
Matter matter = chunk.getOrCreate(y >> 4);
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
}
Matter matter = chunk.getOrCreate(y >> 4);
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
} else {
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
public <T> T getData(int x, int y, int z, Class<T> type) {
int cx = x >> 4;
int cz = z >> 4;
if (y < 0 || y >= mantle.getWorldHeight()) {
return null;
}
MantleChunk chunk = acquireChunk(cx, cz);
if (chunk == null) {
return null;
}
return chunk.getOrCreate(y >> 4)
.<T>slice(type)
.get(x & 15, y & 15, z & 15);
}
@ChunkCoordinates
public MantleChunk acquireChunk(int cx, int cz) {
if (cx < this.x - radius || cx > this.x + radius
|| cz < this.z - radius || cz > this.z + radius) {
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
return null;
}
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
if (chunk == null) Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
return chunk;
}
@Override
@@ -180,7 +207,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
@Override
public BlockData get(int x, int y, int z) {
return getEngineMantle().get(x, y, z);
BlockData block = getData(x, y, z, BlockData.class);
if (block == null)
return AIR;
return block;
}
@Override
@@ -190,12 +220,12 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
@Override
public boolean isCarved(int x, int y, int z) {
return getEngineMantle().isCarved(x, y, z);
return getData(x, y, z, MatterCavern.class) != null;
}
@Override
public boolean isSolid(int x, int y, int z) {
return getEngineMantle().isSolid(x, y, z);
return B.isSolid(get(x, y, z));
}
@Override
@@ -215,7 +245,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
@Override
public void setTile(int xx, int yy, int zz, TileData tile) {
getEngineMantle().setTile(xx, yy, zz, tile);
setData(xx, yy, zz, new TileWrapper(tile));
}
@Override

View File

@@ -28,17 +28,13 @@ import com.volmit.iris.engine.object.IrisCarving;
import com.volmit.iris.engine.object.IrisRegion;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.ReservedFlag;
import com.volmit.iris.util.math.RNG;
import lombok.Getter;
@Getter
@ComponentFlag(MantleFlag.CARVED)
@ComponentFlag(ReservedFlag.CARVED)
public class MantleCarvingComponent extends IrisMantleComponent {
private final int radius = computeRadius();
public MantleCarvingComponent(EngineMantle engineMantle) {
super(engineMantle, MantleFlag.CARVED, 0);
super(engineMantle, ReservedFlag.CARVED, 0);
}
@Override
@@ -63,7 +59,7 @@ public class MantleCarvingComponent extends IrisMantleComponent {
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
}
private int computeRadius() {
protected int computeRadius() {
var dimension = getDimension();
int max = 0;

View File

@@ -28,17 +28,13 @@ import com.volmit.iris.engine.object.IrisFluidBodies;
import com.volmit.iris.engine.object.IrisRegion;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.ReservedFlag;
import com.volmit.iris.util.math.RNG;
import lombok.Getter;
@Getter
@ComponentFlag(MantleFlag.FLUID_BODIES)
@ComponentFlag(ReservedFlag.FLUID_BODIES)
public class MantleFluidBodyComponent extends IrisMantleComponent {
private final int radius = computeRadius();
public MantleFluidBodyComponent(EngineMantle engineMantle) {
super(engineMantle, MantleFlag.FLUID_BODIES, 0);
super(engineMantle, ReservedFlag.FLUID_BODIES, 0);
}
@Override
@@ -63,7 +59,7 @@ public class MantleFluidBodyComponent extends IrisMantleComponent {
bodies.generate(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4);
}
private int computeRadius() {
protected int computeRadius() {
int max = 0;
max = Math.max(max, getDimension().getFluidBodies().getMaxRange(getData()));

View File

@@ -30,7 +30,7 @@ import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.ReservedFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
@@ -40,14 +40,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
@ComponentFlag(MantleFlag.JIGSAW)
@ComponentFlag(ReservedFlag.JIGSAW)
public class MantleJigsawComponent extends IrisMantleComponent {
@Getter
private final int radius = computeRadius();
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, MantleFlag.JIGSAW, 1);
super(engineMantle, ReservedFlag.JIGSAW, 2);
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
}
@@ -176,10 +174,14 @@ public class MantleJigsawComponent extends IrisMantleComponent {
return getEngineMantle().getEngine().getSeedManager().getJigsaw();
}
private int computeRadius() {
protected int computeRadius() {
var dimension = getDimension();
KSet<String> structures = new KSet<>();
if (dimension.getStronghold() != null) {
structures.add(dimension.getStronghold());
}
for (var placement : dimension.getJigsawStructures()) {
structures.add(placement.getStructure());
}

View File

@@ -33,13 +33,12 @@ import com.volmit.iris.util.data.B;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.ReservedFlag;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterStructurePOI;
import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.noise.NoiseType;
import com.volmit.iris.util.parallel.BurstExecutor;
import lombok.Getter;
import org.bukkit.util.BlockVector;
import java.io.IOException;
@@ -47,13 +46,11 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@Getter
@ComponentFlag(MantleFlag.OBJECT)
@ComponentFlag(ReservedFlag.OBJECT)
public class MantleObjectComponent extends IrisMantleComponent {
private final int radius = computeRadius();
public MantleObjectComponent(EngineMantle engineMantle) {
super(engineMantle, MantleFlag.OBJECT, 1);
super(engineMantle, ReservedFlag.OBJECT, 1);
}
@Override
@@ -157,7 +154,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
return v;
}
private int computeRadius() {
protected int computeRadius() {
var dimension = getDimension();
AtomicInteger xg = new AtomicInteger();

View File

@@ -27,6 +27,7 @@ import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.Mantle;
@@ -53,10 +54,11 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
}
@Override
@ChunkCoordinates
public void onModify(int x, int z, Hunk<BlockData> output, boolean multicore, ChunkContext context) {
PrecisionStopwatch p = PrecisionStopwatch.start();
Mantle mantle = getEngine().getMantle().getMantle();
MantleChunk mc = getEngine().getMantle().getMantle().getChunk(x, z).use();
MantleChunk mc = mantle.getChunk(x, z).use();
KMap<Long, KList<Integer>> positions = new KMap<>();
KMap<IrisPosition, MatterCavern> walls = new KMap<>();
Consumer4<Integer, Integer, Integer, MatterCavern> iterator = (xx, yy, zz, c) -> {
@@ -81,19 +83,19 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
//todo: Fix chunk decoration not working on chunk's border
if (rz < 15 && mantle.get(xx, yy, zz + 1, MatterCavern.class) == null) {
if (rz < 15 && mc.get(xx, yy, zz + 1, MatterCavern.class) == null) {
walls.put(new IrisPosition(rx, yy, rz + 1), c);
}
if (rx < 15 && mantle.get(xx + 1, yy, zz, MatterCavern.class) == null) {
if (rx < 15 && mc.get(xx + 1, yy, zz, MatterCavern.class) == null) {
walls.put(new IrisPosition(rx + 1, yy, rz), c);
}
if (rz > 0 && mantle.get(xx, yy, zz - 1, MatterCavern.class) == null) {
if (rz > 0 && mc.get(xx, yy, zz - 1, MatterCavern.class) == null) {
walls.put(new IrisPosition(rx, yy, rz - 1), c);
}
if (rx > 0 && mantle.get(xx - 1, yy, zz, MatterCavern.class) == null) {
if (rx > 0 && mc.get(xx - 1, yy, zz, MatterCavern.class) == null) {
walls.put(new IrisPosition(rx - 1, yy, rz), c);
}
@@ -217,16 +219,21 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
if (!blocks.hasIndex(i)) {
break;
}
int y = zone.floor - i - 1;
if (!B.isSolid(output.get(rx, zone.floor - i - 1, rz))) {
BlockData b = blocks.get(i);
BlockData down = output.get(rx, y, rz);
if (!B.isSolid(down)) {
continue;
}
if (B.isOre(output.get(rx, zone.floor - i - 1, rz))) {
if (B.isOre(down)) {
output.set(rx, y, rz, B.toDeepSlateOre(down, b));
continue;
}
output.set(rx, zone.floor - i - 1, rz, blocks.get(i));
output.set(rx, y, rz, blocks.get(i));
}
blocks = biome.generateCeilingLayers(getDimension(), xx, zz, rng, 3, zone.ceiling, getData(), getComplex());

View File

@@ -5,7 +5,7 @@ import com.volmit.iris.engine.framework.EngineAssignedModifier;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.data.IrisCustomData;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import org.bukkit.block.data.BlockData;

View File

@@ -25,9 +25,12 @@ import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.data.HeightMap;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterCavern;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.BlockVector;
@@ -53,28 +56,30 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
long seed = x * 341873128712L + z * 132897987541L;
long mask = 0;
MantleChunk chunk = getEngine().getMantle().getMantle().getChunk(x, z).use();
for (IrisDepositGenerator k : getDimension().getDeposits()) {
long finalSeed = seed * ++mask;
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
burst.queue(() -> generate(k, chunk, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
}
for (IrisDepositGenerator k : region.getDeposits()) {
long finalSeed = seed * ++mask;
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
burst.queue(() -> generate(k, chunk, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
}
for (IrisDepositGenerator k : biome.getDeposits()) {
long finalSeed = seed * ++mask;
burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
burst.queue(() -> generate(k, chunk, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context));
}
burst.complete();
chunk.release();
}
public void generate(IrisDepositGenerator k, Hunk<BlockData> data, RNG rng, int cx, int cz, boolean safe, ChunkContext context) {
generate(k, data, rng, cx, cz, safe, null, context);
public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk<BlockData> data, RNG rng, int cx, int cz, boolean safe, ChunkContext context) {
generate(k, chunk, data, rng, cx, cz, safe, null, context);
}
public void generate(IrisDepositGenerator k, Hunk<BlockData> data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) {
public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk<BlockData> data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) {
if (k.getSpawnChance() < rng.d())
return;
@@ -122,8 +127,11 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
if (ny > height || nx > 15 || nx < 0 || ny > getEngine().getHeight() || ny < 0 || nz < 0 || nz > 15) {
continue;
}
if (!k.isReplaceBedrock() && data.get(nx, ny, nz).getMaterial() == Material.BEDROCK) {
continue;
}
if (!getEngine().getMantle().isCarved((cx << 4) + nx, ny, (cz << 4) + nz)) {
if (chunk.get(nx, ny, nz, MatterCavern.class) == null) {
data.set(nx, ny, nz, B.toDeepSlateOre(data.get(nx, ny, nz), clump.getBlocks().get(j)));
}
}

View File

@@ -25,9 +25,9 @@ import com.volmit.iris.util.stream.interpolation.Interpolated;
import java.util.List;
public interface IRare {
static <T extends IRare> ProceduralStream<T> stream(ProceduralStream<Double> noise, List<T> possibilities) {
return ProceduralStream.of((x, z) -> pick(possibilities, noise.get(x, z)),
(x, y, z) -> pick(possibilities, noise.get(x, y, z)),
static <T extends IRare> ProceduralStream<T> stream(ProceduralStream<Double> noise, List<T> possibilities, boolean legacyRarity) {
return ProceduralStream.of(legacyRarity ? (x, z) -> pickLegacy(possibilities, noise.get(x, z)) : (x, z) -> pick(possibilities, noise.get(x, z)),
legacyRarity ? (x, y, z) -> pickLegacy(possibilities, noise.get(x, y, z)) : (x, y, z) -> pick(possibilities, noise.get(x, y, z)),
new Interpolated<T>() {
@Override
public double toDouble(T t) {
@@ -69,6 +69,32 @@ public interface IRare {
return null;
}
if (possibilities.size() == 1) {
return possibilities.getFirst();
}
double total = 0;
for (T i : possibilities) {
total += 1d / i.getRarity();
}
double threshold = total * noiseValue;
double buffer = 0;
for (T i : possibilities) {
buffer += 1d / i.getRarity();
if (buffer >= threshold) {
return i;
}
}
return possibilities.getLast();
}
static <T extends IRare> T pickLegacy(List<T> possibilities, double noiseValue) {
if (possibilities.isEmpty()) {
return null;
}
if (possibilities.size() == 1) {
return possibilities.get(0);
}

View File

@@ -143,14 +143,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@Desc("The default wall if iris decides to place a wall higher than 2 blocks (steep hills or possibly cliffs)")
private IrisBiomePaletteLayer wall = new IrisBiomePaletteLayer().zero();
@Required
@ArrayType(min = 1, type = IrisBiomePaletteLayer.class)
@ArrayType(type = IrisBiomePaletteLayer.class)
@Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.")
private KList<IrisBiomePaletteLayer> layers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer());
@Required
@ArrayType(min = 1, type = IrisBiomePaletteLayer.class)
@ArrayType(type = IrisBiomePaletteLayer.class)
@Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.")
private KList<IrisBiomePaletteLayer> caveCeilingLayers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer());
@ArrayType(min = 1, type = IrisBiomePaletteLayer.class)
@ArrayType(type = IrisBiomePaletteLayer.class)
@Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.")
private KList<IrisBiomePaletteLayer> seaLayers = new KList<>();
@ArrayType(min = 1, type = IrisDecorator.class)

View File

@@ -61,6 +61,7 @@ public class IrisBlockData extends IrisRegistrant {
private int weight = 1;
@Desc("If the block cannot be created on this version, Iris will attempt to use this backup block data instead.")
private IrisBlockData backup = null;
@RegistryMapBlockState("block")
@Desc("Optional properties for this block data such as 'waterlogged': true")
private KMap<String, Object> data = new KMap<>();
@Desc("Optional tile data for this block data")

View File

@@ -104,7 +104,7 @@ public class IrisCave extends IrisRegistrant {
CNG cng = shape.getNoise(base.nextParallelRNG(8131545), engine);
KSet<IrisPosition> mask = shape.getMasked(rng, engine);
writer.setNoiseMasked(points,
girth, cng.noise(x, y, z), cng, mask, true,
girth, shape.getNoiseThreshold() < 0 ? cng.noise(x, y, z) : shape.getNoiseThreshold(), cng, mask, true,
(xf, yf, zf) -> yf <= h ? w : c);
}

View File

@@ -1,9 +1,7 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.math.M;
@@ -25,6 +23,11 @@ public class IrisCaveShape {
@Desc("Noise used for the shape of the cave")
private IrisGeneratorStyle noise = new IrisGeneratorStyle();
@MinNumber(0)
@MaxNumber(1)
@Desc("The threshold for noise mask")
private double noiseThreshold = -1;
@RegistryListResource(IrisObject.class)
@Desc("Object used as mask for the shape of the cave")
private String object = null;

View File

@@ -21,6 +21,7 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.annotations.ArrayType;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.Required;
import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.util.collection.KList;
import lombok.Data;
@@ -37,6 +38,7 @@ import org.bukkit.World;
@Data
public class IrisCommand {
@Required
@ArrayType(min = 1, type = String.class)
@Desc("List of commands. Iris replaces {x} {y} and {z} with the location of the entity spawn")
private KList<String> commands = new KList<>();

View File

@@ -33,6 +33,7 @@ import org.bukkit.entity.Player;
@Desc("Represents a casting location for a command")
@Data
public class IrisCommandRegistry {
@Required
@ArrayType(min = 1, type = IrisCommand.class)
@Desc("Run commands, at the exact location of the player")
private KList<IrisCommand> rawCommands = new KList<>();

View File

@@ -87,6 +87,8 @@ public class IrisDepositGenerator {
@MaxNumber(64)
@Desc("Ore varience is how many different objects clumps iris will create")
private int varience = 3;
@Desc("If set to true, this deposit will replace bedrock")
private boolean replaceBedrock = false;
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) {
KList<IrisObject> objects = this.objects.aquire(() ->

View File

@@ -30,11 +30,12 @@ import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.engine.object.annotations.functions.ComponentFlagFunction;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.DataProvider;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.noise.CNG;
@@ -68,6 +69,7 @@ public class IrisDimension extends IrisRegistrant {
private final transient AtomicCache<Double> rad = new AtomicCache<>();
private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>();
private final transient AtomicCache<KList<Position2>> strongholdsCache = new AtomicCache<>();
private final transient AtomicCache<KMap<String, KList<String>>> cachedPreProcessors = new AtomicCache<>();
@MinNumber(2)
@Required
@Desc("The human readable name of this dimension")
@@ -241,9 +243,22 @@ public class IrisDimension extends IrisRegistrant {
@Desc("The Subterrain Fluid Layer Height")
private int caveLavaHeight = 8;
@RegistryListFunction(ComponentFlagFunction.class)
@ArrayType(type = MantleFlag.class)
@ArrayType(type = String.class)
@Desc("Collection of disabled components")
private KList<MantleFlag> disabledComponents = new KList<>();
@Desc("A list of globally applied pre-processors")
@ArrayType(type = IrisPreProcessors.class)
private KList<IrisPreProcessors> globalPreProcessors = new KList<>();
@Desc("A list of scripts executed on engine setup")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> engineScripts = new KList<>();
@Desc("A list of scripts executed on data setup")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> dataScripts = new KList<>();
@Desc("Use legacy rarity instead of modern one\nWARNING: Changing this may break expressions and image maps")
private boolean legacyRarity = true;
public int getMaxHeight() {
return (int) getDimensionHeight().getMax();
@@ -364,6 +379,17 @@ public class IrisDimension extends IrisRegistrant {
return r;
}
public KList<String> getPreProcessors(String type) {
return cachedPreProcessors.aquire(() -> {
KMap<String, KList<String>> preProcessors = new KMap<>();
for (var entry : globalPreProcessors) {
preProcessors.computeIfAbsent(entry.getType(), k -> new KList<>())
.addAll(entry.getScripts());
}
return preProcessors;
}).get(type);
}
public IrisGeneratorStyle getBiomeStyle(InferredType type) {
switch (type) {
case CAVE:

View File

@@ -18,7 +18,10 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineMode;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.engine.object.annotations.Snippet;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -35,4 +38,19 @@ public class IrisDimensionMode {
@Desc("The dimension type")
private IrisDimensionModeType type = IrisDimensionModeType.OVERWORLD;
@RegistryListResource(IrisScript.class)
@Desc("The script to create the dimension mode instead of using provided types")
private String script;
public EngineMode create(Engine engine) {
if (script == null) {
return type.create(engine);
}
Object result = engine.getExecution().evaluate(script);
if (result instanceof EngineMode) {
return (EngineMode) result;
}
throw new IllegalStateException("The script '" + script + "' did not return an engine mode!");
}
}

View File

@@ -145,7 +145,6 @@ public class IrisEffect {
@MinNumber(1)
@Desc("The chance is 1 in CHANCE per interval")
private int chance = 50;
@ArrayType(min = 1, type = IrisCommandRegistry.class)
@Desc("Run commands, with configurable location parameters")
private IrisCommandRegistry commandRegistry = null;

View File

@@ -205,9 +205,8 @@ public class IrisEntity extends IrisRegistrant {
if (!spawnerScript.isEmpty() && ee == null) {
synchronized (this) {
gen.getExecution().getAPI().setLocation(at.clone());
try {
ee = (Entity) gen.getExecution().evaluate(spawnerScript);
ee = (Entity) gen.getExecution().spawnMob(spawnerScript, at.clone());
} catch (Throwable ex) {
Iris.error("You must return an Entity in your scripts to use entity scripts!");
ex.printStackTrace();
@@ -332,11 +331,8 @@ public class IrisEntity extends IrisRegistrant {
if (postSpawnScripts.isNotEmpty()) {
synchronized (this) {
gen.getExecution().getAPI().setLocation(at);
gen.getExecution().getAPI().setEntity(ee);
for (String i : postSpawnScripts) {
gen.getExecution().execute(i);
gen.getExecution().postSpawnMob(i, at, ee);
}
}
}

View File

@@ -18,6 +18,7 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*;
@@ -25,6 +26,7 @@ import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.noise.ExpressionNoise;
import com.volmit.iris.util.noise.ImageNoise;
import com.volmit.iris.util.noise.NoiseGenerator;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -56,6 +58,9 @@ public class IrisGeneratorStyle {
private String expression = null;
@Desc("Use an Image map instead of a generated value")
private IrisImageMap imageMap = null;
@Desc("Instead of using the style property, use a custom noise generator to represent this style.")
@RegistryListResource(IrisScript.class)
private String script = null;
@MinNumber(0.00001)
@Desc("The Output multiplier. Only used if parent is fracture.")
private double multiplier = 1;
@@ -93,40 +98,27 @@ public class IrisGeneratorStyle {
public CNG createNoCache(RNG rng, IrisData data, boolean actuallyCached) {
String cacheKey = hash() + "";
CNG cng = null;
if (getExpression() != null) {
IrisExpression e = data.getExpressionLoader().load(getExpression());
if (e != null) {
CNG cng = new CNG(rng, new ExpressionNoise(rng, e), 1D, 1)
.bake().scale(1D / zoom).pow(exponent).bake();
cng.setTrueFracturing(axialFracturing);
if (fracture != null) {
cng.fractureWith(fracture.create(rng.nextParallelRNG(2934), data), fracture.getMultiplier());
}
if (cellularFrequency > 0) {
return cng.cellularize(rng.nextParallelRNG(884466), cellularFrequency).scale(1D / cellularZoom).bake();
}
return cng;
cng = new CNG(rng, new ExpressionNoise(rng, e), 1D, 1).bake();
}
} else if (getImageMap() != null) {
CNG cng = new CNG(rng, new ImageNoise(data, getImageMap()), 1D, 1).bake().scale(1D / zoom).pow(exponent).bake();
cng.setTrueFracturing(axialFracturing);
if (fracture != null) {
cng.fractureWith(fracture.create(rng.nextParallelRNG(2934), data), fracture.getMultiplier());
cng = new CNG(rng, new ImageNoise(data, getImageMap()), 1D, 1).bake();
} else if (getScript() != null) {
Object result = data.getEnvironment().createNoise(getScript(), rng);
if (result == null) Iris.warn("Failed to create noise from script: " + getScript());
if (result instanceof NoiseGenerator generator) {
cng = new CNG(rng, generator, 1D, 1).bake();
}
if (cellularFrequency > 0) {
return cng.cellularize(rng.nextParallelRNG(884466), cellularFrequency).scale(1D / cellularZoom).bake();
}
return cng;
}
CNG cng = style.create(rng).bake().scale(1D / zoom).pow(exponent).bake();
if (cng == null) {
cng = style.create(rng).bake();
}
cng = cng.scale(1D / zoom).pow(exponent).bake();
cng.setTrueFracturing(axialFracturing);
if (fracture != null) {

View File

@@ -50,8 +50,7 @@ public class IrisJigsawPiece extends IrisRegistrant {
@Desc("The object this piece represents")
private String object = "";
@Required
@ArrayType(type = IrisJigsawPieceConnector.class, min = 1)
@ArrayType(type = IrisJigsawPieceConnector.class)
@Desc("The connectors this object contains")
private KList<IrisJigsawPieceConnector> connectors = new KList<>();

View File

@@ -514,9 +514,9 @@ public class IrisObject extends IrisRegistrant {
max.setZ(Math.max(max.getZ(), i.getZ()));
}
w = max.getBlockX() - min.getBlockX() + (min.getBlockX() <= 0 && max.getBlockX() >= 0 && min.getBlockX() != max.getBlockX() ? 1 : 0);
h = max.getBlockY() - min.getBlockY() + (min.getBlockY() <= 0 && max.getBlockY() >= 0 && min.getBlockY() != max.getBlockY() ? 1 : 0);
d = max.getBlockZ() - min.getBlockZ() + (min.getBlockZ() <= 0 && max.getBlockZ() >= 0 && min.getBlockZ() != max.getBlockZ() ? 1 : 0);
w = max.getBlockX() - min.getBlockX() + 1;
h = max.getBlockY() - min.getBlockY() + 1;
d = max.getBlockZ() - min.getBlockZ() + 1;
center = new BlockVector(w / 2, h / 2, d / 2);
}

View File

@@ -0,0 +1,25 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.engine.object.annotations.functions.ResourceLoadersFunction;
import com.volmit.iris.util.collection.KList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Desc("Represents global preprocessors")
public class IrisPreProcessors {
@Required
@Desc("The preprocessor type")
@RegistryListFunction(ResourceLoadersFunction.class)
private String type = "dimension";
@Required
@Desc("The preprocessor scripts")
@RegistryListResource(IrisScript.class)
@ArrayType(type = String.class, min = 1)
private KList<String> scripts = new KList<>();
}

View File

@@ -0,0 +1,12 @@
package com.volmit.iris.engine.object.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RegistryMapBlockState {
String value();
}

View File

@@ -6,7 +6,7 @@ import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.engine.mantle.ComponentFlag;
import com.volmit.iris.engine.mantle.MantleComponent;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import java.util.Objects;

View File

@@ -0,0 +1,28 @@
package com.volmit.iris.engine.object.annotations.functions;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.util.collection.KList;
public class ResourceLoadersFunction implements ListFunction<KList<String>> {
@Override
public String key() {
return "resource-loader";
}
@Override
public String fancyName() {
return "Resource Loader";
}
@Override
public KList<String> apply(IrisData data) {
return data.getLoaders()
.values()
.stream()
.filter(rl -> ResourceLoader.class.equals(rl.getClass()))
.map(ResourceLoader::getFolderName)
.collect(KList.collector());
}
}

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.platform;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
@@ -34,7 +35,6 @@ import com.volmit.iris.engine.object.StudioMode;
import com.volmit.iris.engine.platform.studio.StudioGenerator;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.data.IrisBiomeStorage;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
import com.volmit.iris.util.io.ReactiveFolder;
@@ -45,8 +45,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -205,7 +203,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
IrisBiomeStorage st = new IrisBiomeStorage();
TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
this.world.bind(world);
getEngine().generate(x << 4, z << 4, tc, false);
getEngine().generate(x << 4, z << 4, tc, IrisSettings.get().getGenerator().useMulticore);
Chunk c = Iris.platform.getChunkAtAsync(world, x, z)
.thenApply(d -> {
@@ -367,7 +365,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
} else {
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc);
BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight());
getEngine().generate(x << 4, z << 4, blocks, biomes, false);
getEngine().generate(x << 4, z << 4, blocks, biomes, IrisSettings.get().getGenerator().useMulticore);
blocks.apply();
biomes.apply();
}

View File

@@ -1,38 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.engine.scripting;
import com.volmit.iris.engine.framework.Engine;
import org.apache.bsf.BSFManager;
public interface EngineExecutionEnvironment {
Engine getEngine();
IrisScriptingAPI getAPI();
BSFManager getManager();
void execute(String script);
Object evaluate(String script);
default void close() {
}
}

View File

@@ -1,96 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.engine.scripting;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisExpression;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
@Data
@EqualsAndHashCode(exclude = "engine")
@ToString(exclude = "engine")
public class IrisScriptingAPI {
private final Engine engine;
private IrisRegistrant preprocessorObject;
private double x = 0;
private double y = 0;
private double z = 0;
private Location location;
private Entity entity;
public IrisScriptingAPI(Engine engine) {
this.engine = engine;
}
public IrisData getData() {
return getEngine().getData();
}
public IrisComplex getComplex() {
return getEngine().getComplex();
}
public long getSeed() {
return getEngine().getSeedManager().getScript();
}
public double expression(String expressionName, double x, double y, double z) {
IrisExpression expression = getData().getExpressionLoader().load(expressionName);
return expression.evaluate(getComplex().getRng(), x, y, z);
}
public double expression(String expressionName, double x, double z) {
IrisExpression expression = getData().getExpressionLoader().load(expressionName);
return expression.evaluate(getComplex().getRng(), x, z);
}
public IrisBiome getBiomeAt(int x, int z) {
return getEngine().getSurfaceBiome(x, z);
}
public IrisDimension getDimension() {
return getEngine().getDimension();
}
public void info(String log) {
Iris.info(log);
}
public void debug(String log) {
Iris.debug(log);
}
public void warn(String log) {
Iris.warn(log);
}
public void error(String log) {
Iris.error(log);
}
}

View File

@@ -29,7 +29,7 @@ import lombok.Data;
@Data
public class IrisContext {
private static final KMap<Thread, IrisContext> context = new KMap<>();
private static ChronoLatch cl = new ChronoLatch(60000);
private static final ChronoLatch cl = new ChronoLatch(60000);
private final Engine engine;
private ChunkContext chunkContext;
@@ -53,9 +53,10 @@ public class IrisContext {
}
public static void touch(IrisContext c) {
synchronized (context) {
context.put(Thread.currentThread(), c);
context.put(Thread.currentThread(), c);
if (!cl.couldFlip()) return;
synchronized (cl) {
if (cl.flip()) {
dereference();
}
@@ -63,15 +64,13 @@ public class IrisContext {
}
public static void dereference() {
synchronized (context) {
for (Thread i : context.k()) {
if (!i.isAlive() || context.get(i).engine.isClosed()) {
if (context.get(i).engine.isClosed()) {
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.getId());
}
context.remove(i);
for (Thread i : context.k()) {
if (!i.isAlive() || context.get(i).engine.isClosed()) {
if (context.get(i).engine.isClosed()) {
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.threadId());
}
context.remove(i);
}
}
}

View File

@@ -22,6 +22,8 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.link.data.DataType;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.BlockProperty;
import com.volmit.iris.core.service.ExternalDataSVC;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
@@ -35,10 +37,7 @@ import org.bukkit.block.data.Waterlogged;
import org.bukkit.block.data.type.Leaves;
import org.bukkit.block.data.type.PointedDripstone;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
import static org.bukkit.Material.*;
@@ -610,8 +609,7 @@ public class B {
}
public static boolean isUpdatable(BlockData mat) {
return isLit(mat)
|| isStorage(mat)
return isStorage(mat)
|| (mat instanceof PointedDripstone
&& ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP));
}
@@ -681,6 +679,28 @@ public class B {
return bt.toArray(new String[0]);
}
public synchronized static KMap<List<String>, List<BlockProperty>> getBlockStates() {
KMap<List<BlockProperty>, List<String>> flipped = new KMap<>();
INMS.get().getBlockProperties().forEach((k, v) -> {
flipped.computeIfAbsent(v, $ -> new KList<>()).add(k.getKey().toString());
});
var emptyStates = flipped.computeIfAbsent(new KList<>(0), $ -> new KList<>());
for (var pair : Iris.service(ExternalDataSVC.class).getAllBlockProperties()) {
if (pair.getB().isEmpty()) emptyStates.add(pair.getA().toString());
else flipped.computeIfAbsent(pair.getB(), $ -> new KList<>()).add(pair.getA().toString());
}
emptyStates.addAll(custom.k());
KMap<List<String>, List<BlockProperty>> states = new KMap<>();
flipped.forEach((k, v) -> {
var old = states.put(v, k);
if (old != null) Iris.error("Duplicate block state: " + v + " (" + old + " and " + k + ")");
});
return states;
}
public static String[] getItemTypes() {
KList<String> bt = new KList<>();

View File

@@ -21,10 +21,16 @@ package com.volmit.iris.util.data;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.volmit.iris.engine.framework.MeteredCache;
import com.volmit.iris.util.math.RollingSequence;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class KCache<K, V> implements MeteredCache {
public static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final long max;
private final LoadingCache<K, V> cache;
private final boolean fastDump;
@@ -46,6 +52,8 @@ public class KCache<K, V> implements MeteredCache {
return Caffeine
.newBuilder()
.maximumSize(max)
.scheduler(Scheduler.systemScheduler())
.executor(EXECUTOR)
.initialCapacity((int) (max))
.build((k) -> loader == null ? null : loader.load(k));
}

View File

@@ -19,12 +19,12 @@
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint;
import lombok.Getter;
import org.apache.commons.lang3.Validate;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.function.IntConsumer;
@@ -52,8 +52,10 @@ public class DataBits {
0, 5};
private final AtomicLongArray data;
@Getter
private final int bits;
private final long mask;
@Getter
private final int size;
private final int valuesPerLong;
private final int divideMul;
@@ -149,18 +151,9 @@ public class DataBits {
return data;
}
public int getSize() {
return size;
}
public int getBits() {
return bits;
}
public DataBits setBits(int newBits) {
if (bits != newBits) {
DataBits newData = new DataBits(newBits, size);
AtomicInteger c = new AtomicInteger(0);
for (int i = 0; i < size; i++) {
newData.set(i, get(i));

View File

@@ -19,78 +19,32 @@
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint;
import lombok.Synchronized;
import java.io.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class DataContainer<T> {
protected static final int INITIAL_BITS = 3;
protected static final int LINEAR_BITS_LIMIT = 4;
protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1;
protected static final int[] BIT = computeBitLimits();
private final AtomicReference<Palette<T>> palette;
private final AtomicReference<DataBits> data;
private final AtomicInteger bits;
private volatile Palette<T> palette;
private volatile DataBits data;
private final int length;
private final Writable<T> writer;
public DataContainer(Writable<T> writer, int length) {
this.writer = writer;
this.length = length;
this.bits = new AtomicInteger(INITIAL_BITS);
this.data = new AtomicReference<>(new DataBits(INITIAL_BITS, length));
this.palette = new AtomicReference<>(newPalette(INITIAL_BITS));
this.data = new DataBits(INITIAL_BITS, length);
this.palette = newPalette(INITIAL_BITS);
}
public DataContainer(DataInputStream din, Writable<T> writer) throws IOException {
this.writer = writer;
this.length = Varint.readUnsignedVarInt(din);
this.palette = new AtomicReference<>(newPalette(din));
this.data = new AtomicReference<>(new DataBits(palette.get().bits(), length, din));
this.bits = new AtomicInteger(palette.get().bits());
}
public static String readBitString(DataInputStream din) throws IOException {
DataContainer<Character> c = new DataContainer<>(din, new Writable<Character>() {
@Override
public Character readNodeData(DataInputStream din) throws IOException {
return din.readChar();
}
@Override
public void writeNodeData(DataOutputStream dos, Character character) throws IOException {
dos.writeChar(character);
}
});
StringBuilder sb = new StringBuilder();
for (int i = c.size() - 1; i >= 0; i--) {
sb.setCharAt(i, c.get(i));
}
return sb.toString();
}
public static void writeBitString(String s, DataOutputStream dos) throws IOException {
DataContainer<Character> c = new DataContainer<>(new Writable<Character>() {
@Override
public Character readNodeData(DataInputStream din) throws IOException {
return din.readChar();
}
@Override
public void writeNodeData(DataOutputStream dos, Character character) throws IOException {
dos.writeChar(character);
}
}, s.length());
for (int i = 0; i < s.length(); i++) {
c.set(i, s.charAt(i));
}
c.writeDos(dos);
this.palette = newPalette(din);
this.data = new DataBits(palette.bits(), length, din);
}
private static int[] computeBitLimits() {
@@ -117,17 +71,9 @@ public class DataContainer<T> {
return DataContainer.BIT.length - 1;
}
public DataBits getData() {
return data.get();
}
public Palette<T> getPalette() {
return palette.get();
}
public String toString() {
return "DataContainer <" + length + " x " + bits + " bits> -> Palette<" + palette.get().getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.get().size() +
" " + data.get().toString() + " PalBit: " + palette.get().bits();
return "DataContainer <" + length + " x " + data.getBits() + " bits> -> Palette<" + palette.getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.size() +
" " + data.toString() + " PalBit: " + palette.bits();
}
public byte[] write() throws IOException {
@@ -140,11 +86,12 @@ public class DataContainer<T> {
writeDos(new DataOutputStream(out));
}
@Synchronized
public void writeDos(DataOutputStream dos) throws IOException {
Varint.writeUnsignedVarInt(length, dos);
Varint.writeUnsignedVarInt(palette.get().size(), dos);
palette.get().iterateIO((data, __) -> writer.writeNodeData(dos, data));
data.get().write(dos);
Varint.writeUnsignedVarInt(palette.size(), dos);
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
data.write(dos);
dos.flush();
}
@@ -163,55 +110,44 @@ public class DataContainer<T> {
return new HashPalette<>();
}
public void ensurePaletted(T t) {
if (palette.get().id(t) == -1) {
expandOne();
}
}
@Synchronized
public void set(int position, T t) {
synchronized (this) {
int id = palette.get().id(t);
int id = palette.id(t);
if (id == -1) {
expandOne();
id = palette.get().add(t);
}
data.get().set(position, id);
if (id == -1) {
id = palette.add(t);
updateBits();
}
data.set(position, id);
}
private void expandOne() {
if (palette.get().size() + 1 >= BIT[bits.get()]) {
setBits(bits.get() + 1);
@Synchronized
@SuppressWarnings("NonAtomicOperationOnVolatileField")
private void updateBits() {
if (palette.bits() == data.getBits())
return;
int bits = palette.bits();
if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
palette = newPalette(bits).from(palette);
}
data = data.setBits(bits);
}
@Synchronized
public T get(int position) {
synchronized (this) {
int id = data.get().get(position) + 1;
int id = data.get(position);
if (id <= 0) {
return null;
}
return palette.get().get(id - 1);
if (id <= 0) {
return null;
}
}
public void setBits(int bits) {
if (this.bits.get() != bits) {
if (this.bits.get() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
palette.set(newPalette(bits).from(palette.get()));
}
this.bits.set(bits);
data.set(data.get().setBits(bits));
}
return palette.get(id);
}
public int size() {
return getData().getSize();
return data.getSize();
}
}

View File

@@ -21,26 +21,25 @@ package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.function.Consumer2;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class HashPalette<T> implements Palette<T> {
private final ReentrantLock lock = new ReentrantLock();
private final LinkedHashMap<T, Integer> palette;
private final KMap<Integer, T> lookup;
private final AtomicInteger size;
public HashPalette() {
this.size = new AtomicInteger(0);
this.size = new AtomicInteger(1);
this.palette = new LinkedHashMap<>();
this.lookup = new KMap<>();
add(null);
}
@Override
public T get(int id) {
if (id < 0 || id >= size.get()) {
if (id <= 0 || id >= size.get()) {
return null;
}
@@ -49,17 +48,16 @@ public class HashPalette<T> implements Palette<T> {
@Override
public int add(T t) {
lock.lock();
try {
int index = size.getAndIncrement();
palette.put(t, index);
if (t == null) {
return 0;
}
if (t != null) {
synchronized (palette) {
return palette.computeIfAbsent(t, $ -> {
int index = size.getAndIncrement();
lookup.put(index, t);
}
return index;
} finally {
lock.unlock();
return index;
});
}
}
@@ -80,17 +78,33 @@ public class HashPalette<T> implements Palette<T> {
@Override
public void iterate(Consumer2<T, Integer> c) {
lock.lock();
try {
for (T i : palette.keySet()) {
if (i == null) {
continue;
}
c.accept(i, id(i));
synchronized (palette) {
for (int i = 1; i < size.get(); i++) {
c.accept(lookup.get(i), i);
}
} finally {
lock.unlock();
}
}
@Override
public Palette<T> from(Palette<T> oldPalette) {
oldPalette.iterate((t, i) -> {
if (t == null) throw new NullPointerException("Null palette entries are not allowed!");
lookup.put(i, t);
palette.put(t, i);
});
size.set(oldPalette.size() + 1);
return this;
}
@Override
public Palette<T> from(int size, Writable<T> writable, DataInputStream in) throws IOException {
for (int i = 1; i <= size; i++) {
T t = writable.readNodeData(in);
if (t == null) throw new NullPointerException("Null palette entries are not allowed!");
lookup.put(i, t);
palette.put(t, i);
}
this.size.set(size + 1);
return this;
}
}

View File

@@ -19,19 +19,19 @@
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.function.Consumer2;
import lombok.Synchronized;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class LinearPalette<T> implements Palette<T> {
private final AtomicReference<AtomicReferenceArray<T>> palette;
private volatile AtomicReferenceArray<T> palette;
private final AtomicInteger size;
public LinearPalette(int initialSize) {
this.size = new AtomicInteger(0);
this.palette = new AtomicReference<>(new AtomicReferenceArray<>(initialSize));
palette.get().set(size.getAndIncrement(), null);
this.size = new AtomicInteger(1);
this.palette = new AtomicReferenceArray<>(initialSize);
palette.set(0, null);
}
@Override
@@ -40,26 +40,29 @@ public class LinearPalette<T> implements Palette<T> {
return null;
}
return palette.get().get(id);
return palette.get(id);
}
@Override
public int add(T t) {
if (t == null) {
return 0;
}
int index = size.getAndIncrement();
grow(index + 1);
palette.get().set(index, t);
palette.set(index, t);
return index;
}
private void grow(int newLength) {
if (newLength > palette.get().length()) {
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength + size.get());
private synchronized void grow(int newLength) {
if (newLength > palette.length()) {
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength);
for (int i = 0; i < palette.get().length(); i++) {
a.set(i, palette.get().get(i));
for (int i = 0; i < palette.length(); i++) {
a.set(i, palette.get(i));
}
palette.set(a);
palette = a;
}
}
@@ -69,8 +72,8 @@ public class LinearPalette<T> implements Palette<T> {
return 0;
}
for (int i = 1; i < size() + 1; i++) {
if (t.equals(palette.get().get(i))) {
for (int i = 1; i < size.get(); i++) {
if (t.equals(palette.get(i))) {
return i;
}
}
@@ -85,8 +88,8 @@ public class LinearPalette<T> implements Palette<T> {
@Override
public void iterate(Consumer2<T, Integer> c) {
for (int i = 1; i < size() + 1; i++) {
c.accept(palette.get().get(i), i);
for (int i = 1; i <= size(); i++) {
c.accept(palette.get(i), i);
}
}
}

View File

@@ -44,14 +44,14 @@ public class CountingDataInputStream extends DataInputStream {
}
@Override
public int read(@NotNull byte[] b, int off, int len) throws IOException {
public int read(byte @NotNull [] b, int off, int len) throws IOException {
int i = in.read(b, off, len);
count(i);
if (i != -1) count(i);
return i;
}
private void count(int i) {
count += i;
count = Math.addExact(count, i);
if (mark == -1)
return;
@@ -69,6 +69,12 @@ public class CountingDataInputStream extends DataInputStream {
public synchronized void mark(int readlimit) {
if (!in.markSupported()) return;
in.mark(readlimit);
if (readlimit <= 0) {
mark = -1;
markLimit = 0;
return;
}
mark = count;
markLimit = readlimit;
}

View File

@@ -24,14 +24,26 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.volmit.iris.Iris;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.scheduling.J;
import org.apache.commons.io.function.IOConsumer;
import org.apache.commons.io.function.IOFunction;
import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -1663,18 +1675,47 @@ public class IO {
return (ch2 == -1);
}
@SneakyThrows
public static void write(File file, Document doc) {
file.getParentFile().mkdirs();
try (var writer = new FileWriter(file)) {
new XMLWriter(writer, OutputFormat.createPrettyPrint())
.write(doc);
}
}
@SneakyThrows
public static Document read(File file) {
if (file.exists()) return new SAXReader().read(file);
var doc = DocumentHelper.createDocument();
doc.addElement("project")
.addAttribute("version", "4");
return doc;
}
public static <T extends Closeable> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException {
File dir = new File(file.getParentFile(), ".tmp");
dir.mkdirs();
dir.deleteOnExit();
File temp = File.createTempFile("iris",".bin", dir);
try {
try (var target = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.SYNC)) {
lock(target);
try (var out = builder.apply(new FileOutputStream(temp))) {
action.accept(out);
}
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
Files.copy(temp.toPath(), Channels.newOutputStream(target));
} finally {
temp.delete();
}
}
public static FileLock lock(FileChannel channel) throws IOException {
while (true) {
try {
return channel.lock();
} catch (OverlappingFileLockException e) {}
J.sleep(1);
}
}
}

View File

@@ -83,6 +83,28 @@ public class JarScanner {
zip.close();
}
public void scanAll() throws IOException {
classes.clear();
FileInputStream fin = new FileInputStream(jar);
ZipInputStream zip = new ZipInputStream(fin);
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
String c = entry.getName().replaceAll("/", ".").replace(".class", "");
if (c.startsWith(superPackage)) {
try {
Class<?> clazz = Class.forName(c);
classes.add(clazz);
} catch (Throwable e) {
if (!report) continue;
Iris.reportError(e);
e.printStackTrace();
}
}
}
}
}
/**
* Get the scanned clases
*

View File

@@ -35,6 +35,8 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.io.IOWorker;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice;
@@ -44,9 +46,7 @@ import com.volmit.iris.util.parallel.MultiBurst;
import lombok.Getter;
import org.bukkit.Chunk;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -69,6 +69,7 @@ public class Mantle {
private final MultiBurst ioBurst;
private final Semaphore ioTrim;
private final Semaphore ioTectonicUnload;
private final IOWorker worker;
private final AtomicDouble adjustedIdleDuration;
private final KSet<Long> toUnload;
@@ -88,9 +89,10 @@ public class Mantle {
this.ioTectonicUnload = new Semaphore(LOCK_SIZE, true);
loadedRegions = new KMap<>();
lastUse = new KMap<>();
ioBurst = MultiBurst.burst;
ioBurst = MultiBurst.ioBurst;
adjustedIdleDuration = new AtomicDouble(0);
toUnload = new KSet<>();
worker = new IOWorker(dataFolder, worldHeight);
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
}
@@ -379,7 +381,7 @@ public class Mantle {
loadedRegions.forEach((i, plate) -> b.queue(() -> {
try {
plate.close();
plate.write(fileForRegion(dataFolder, i, false));
worker.write(fileForRegion(dataFolder, i, false).getName(), plate);
oldFileForRegion(dataFolder, i).delete();
} catch (Throwable e) {
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
@@ -394,6 +396,11 @@ public class Mantle {
} catch (Throwable e) {
Iris.reportError(e);
}
try {
worker.close();
} catch (Throwable e) {
Iris.reportError(e);
}
IO.delete(new File(dataFolder, ".tmp"));
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
@@ -462,8 +469,8 @@ public class Mantle {
ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE);
try {
double unloadTime = M.ms() - adjustedIdleDuration.get();
for (long id : toUnload) {
double unloadTime = M.ms() - adjustedIdleDuration.get();
burst.queue(() -> hyperLock.withLong(id, () -> {
TectonicPlate m = loadedRegions.get(id);
if (m == null) {
@@ -484,14 +491,15 @@ public class Mantle {
}
try {
m.write(fileForRegion(dataFolder, id, false));
m.close();
worker.write(fileForRegion(dataFolder, id, false).getName(), m);
oldFileForRegion(dataFolder, id).delete();
loadedRegions.remove(id, m);
lastUse.remove(id);
toUnload.remove(id);
i.incrementAndGet();
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
} catch (IOException e) {
} catch (IOException | InterruptedException e) {
Iris.reportError(e);
}
}));
@@ -524,19 +532,17 @@ public class Mantle {
if (!trim || !unload) {
try {
return getSafe(x, z).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
} catch (Throwable e) {
e.printStackTrace();
}
}
} else {
Long key = key(x, z);
TectonicPlate p = loadedRegions.get(key);
Long key = key(x, z);
TectonicPlate p = loadedRegions.get(key);
if (p != null && !p.isClosed()) {
use(key);
return p;
if (p != null && !p.isClosed()) {
use(key);
return p;
}
}
try {
@@ -547,6 +553,9 @@ public class Mantle {
} catch (ExecutionException e) {
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)");
Iris.reportError(e);
} catch (Throwable e) {
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception");
Iris.reportError(e);
}
} finally {
if (trim) ioTrim.release();
@@ -570,17 +579,17 @@ public class Mantle {
return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> {
Long k = key(x, z);
use(k);
TectonicPlate region = loadedRegions.get(k);
if (region != null && !region.isClosed()) {
return region;
TectonicPlate r = loadedRegions.get(k);
if (r != null && !r.isClosed()) {
return r;
}
TectonicPlate region;
File file = fileForRegion(dataFolder, x, z);
if (file.exists()) {
try {
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv."));
region = worker.read(file.getName());
if (region.getX() != x || region.getZ() != z) {
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);

View File

@@ -21,20 +21,23 @@ package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice;
import com.volmit.iris.util.parallel.AtomicBooleanArray;
import lombok.Getter;
import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
@@ -46,7 +49,8 @@ public class MantleChunk {
private final int x;
@Getter
private final int z;
private final AtomicIntegerArray flags;
private final AtomicBooleanArray flags;
private final Object[] flagLocks;
private final AtomicReferenceArray<Matter> sections;
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
private final AtomicBoolean closed = new AtomicBoolean(false);
@@ -59,12 +63,13 @@ public class MantleChunk {
@ChunkCoordinates
public MantleChunk(int sectionHeight, int x, int z) {
sections = new AtomicReferenceArray<>(sectionHeight);
flags = new AtomicIntegerArray(MantleFlag.values().length);
flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1);
flagLocks = new Object[flags.length()];
this.x = x;
this.z = z;
for (int i = 0; i < flags.length(); i++) {
flags.set(i, 0);
flagLocks[i] = new Object();
}
}
@@ -79,10 +84,19 @@ public class MantleChunk {
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
this(sectionHeight, din.readByte(), din.readByte());
int s = din.readByte();
int l = version < 0 ? flags.length() : Varint.readUnsignedVarInt(din);
int l = version < 0 ? MantleFlag.RESERVED_FLAGS : Varint.readUnsignedVarInt(din);
for (int i = 0; i < flags.length() && i < l; i++) {
flags.set(i, din.readBoolean() ? 1 : 0);
if (version >= 1) {
for (int i = 0; i < l;) {
byte f = din.readByte();
for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) {
flags.set(i, (f & (1 << j)) != 0);
}
}
} else {
for (int i = 0; i < flags.length() && i < l; i++) {
flags.set(i, din.readBoolean());
}
}
for (int i = 0; i < s; i++) {
@@ -109,6 +123,9 @@ public class MantleChunk {
din.skipTo(end);
TectonicPlate.addError();
}
if (din.count() != start + size) {
throw new IOException("Chunk section read size mismatch!");
}
}
}
@@ -137,21 +154,35 @@ public class MantleChunk {
ref.release();
}
public void copyFlags(MantleChunk chunk) {
use();
for (int i = 0; i < flags.length(); i++) {
flags.set(i, chunk.flags.get(i));
}
release();
}
public void flag(MantleFlag flag, boolean f) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
flags.set(flag.ordinal(), f ? 1 : 0);
flags.set(flag.ordinal(), f);
}
public void raiseFlag(MantleFlag flag, Runnable r) {
synchronized (this) {
if (!isFlagged(flag)) flag(flag, true);
else return;
raiseFlag(null, flag, r);
}
public void raiseFlag(@Nullable MantleFlag guard, MantleFlag flag, Runnable r) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
if (guard != null && isFlagged(guard)) return;
synchronized (flagLocks[flag.ordinal()]) {
if (flags.compareAndSet(flag.ordinal(), false, true)) {
r.run();
}
}
r.run();
}
public boolean isFlagged(MantleFlag flag) {
return flags.get(flag.ordinal()) == 1;
return flags.get(flag.ordinal());
}
/**
@@ -176,6 +207,15 @@ public class MantleChunk {
return sections.get(section);
}
@Nullable
@ChunkRelativeBlockCoordinates
@SuppressWarnings("unchecked")
public <T> T get(int x, int y, int z, Class<T> type) {
return (T) getOrCreate(y >> 4)
.slice(type)
.get(x & 15, y & 15, z & 15);
}
/**
* Clear all matter from this chunk
*/
@@ -207,7 +247,9 @@ public class MantleChunk {
if (matter == null) {
matter = new IrisMatter(16, 16, 16);
sections.set(section, matter);
if (!sections.compareAndSet(section, null, matter)) {
matter = get(section);
}
}
return matter;
@@ -224,10 +266,15 @@ public class MantleChunk {
dos.writeByte(x);
dos.writeByte(z);
dos.writeByte(sections.length());
Varint.writeUnsignedVarInt(flags.length(), dos);
Varint.writeUnsignedVarInt(Math.ceilDiv(flags.length(), Byte.SIZE), dos);
for (int i = 0; i < flags.length(); i++) {
dos.writeBoolean(flags.get(i) == 1);
int count = flags.length();
for (int i = 0; i < count;) {
int f = 0;
for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) {
f |= flags.get(i) ? (1 << j) : 0;
}
dos.write(f);
}
var bytes = new ByteArrayOutputStream(8192);

View File

@@ -1,44 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.util.mantle;
import com.volmit.iris.util.collection.StateList;
public enum MantleFlag {
OBJECT,
UPDATE,
JIGSAW,
FEATURE,
INITIAL_SPAWNED,
REAL,
CARVED,
FLUID_BODIES,
INITIAL_SPAWNED_MARKER,
CLEANED,
PLANNED,
ETCHED,
TILE,
CUSTOM,
DISCOVERED,
CUSTOM_ACTIVE;
static StateList getStateList() {
return new StateList(MantleFlag.values());
}
}

View File

@@ -19,26 +19,14 @@
package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.engine.EnginePanic;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -49,7 +37,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
public class TectonicPlate {
private static final ThreadLocal<Boolean> errors = ThreadLocal.withInitial(() -> false);
public static final int MISSING = -1;
public static final int CURRENT = 0;
public static final int CURRENT = 1;
private final int sectionHeight;
private final AtomicReferenceArray<MantleChunk> chunks;
@@ -111,31 +99,6 @@ public class TectonicPlate {
}
}
public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException {
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
fc.lock();
InputStream fin = Channels.newInputStream(fc);
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
BufferedInputStream bis = new BufferedInputStream(lz4);
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
return new TectonicPlate(worldHeight, din, versioned);
}
} finally {
if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
fc.lock();
InputStream fin = Channels.newInputStream(fc);
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
errors.remove();
}
}
public boolean inUse() {
for (int i = 0; i < chunks.length(); i++) {
MantleChunk chunk = chunks.get(i);
@@ -223,18 +186,6 @@ public class TectonicPlate {
return Cache.to1D(x, z, 0, 32, 32);
}
/**
* Write this tectonic plate to file
*
* @param file the file to writeNodeData it to
* @throws IOException shit happens
*/
public void write(File file) throws IOException {
PrecisionStopwatch p = PrecisionStopwatch.start();
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write);
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
}
/**
* Write this tectonic plate to a data stream
*
@@ -268,4 +219,12 @@ public class TectonicPlate {
public static void addError() {
errors.set(true);
}
public static boolean hasError() {
try {
return errors.get();
} finally {
errors.remove();
}
}
}

View File

@@ -0,0 +1,28 @@
package com.volmit.iris.util.mantle.flag;
import org.jetbrains.annotations.NotNull;
record CustomFlag(String name, int ordinal) implements MantleFlag {
@Override
public @NotNull String toString() {
return name;
}
@Override
public boolean isCustom() {
return false;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof CustomFlag that))
return false;
return ordinal == that.ordinal;
}
@Override
public int hashCode() {
return ordinal;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.util.mantle.flag;
import org.jetbrains.annotations.Contract;
public sealed interface MantleFlag permits CustomFlag, ReservedFlag {
int MIN_ORDINAL = 64;
int MAX_ORDINAL = 255;
MantleFlag OBJECT = ReservedFlag.OBJECT;
MantleFlag UPDATE = ReservedFlag.UPDATE;
MantleFlag JIGSAW = ReservedFlag.JIGSAW;
MantleFlag FEATURE = ReservedFlag.FEATURE;
MantleFlag INITIAL_SPAWNED = ReservedFlag.INITIAL_SPAWNED;
MantleFlag REAL = ReservedFlag.REAL;
MantleFlag CARVED = ReservedFlag.CARVED;
MantleFlag FLUID_BODIES = ReservedFlag.FLUID_BODIES;
MantleFlag INITIAL_SPAWNED_MARKER = ReservedFlag.INITIAL_SPAWNED_MARKER;
MantleFlag CLEANED = ReservedFlag.CLEANED;
MantleFlag PLANNED = ReservedFlag.PLANNED;
MantleFlag ETCHED = ReservedFlag.ETCHED;
MantleFlag TILE = ReservedFlag.TILE;
MantleFlag CUSTOM = ReservedFlag.CUSTOM;
MantleFlag DISCOVERED = ReservedFlag.DISCOVERED;
MantleFlag CUSTOM_ACTIVE = ReservedFlag.CUSTOM_ACTIVE;
int RESERVED_FLAGS = ReservedFlag.values().length;
String name();
int ordinal();
boolean isCustom();
@Contract(value = "_ -> new", pure = true)
static MantleFlag of(int ordinal) {
if (ordinal < MIN_ORDINAL || ordinal > MAX_ORDINAL)
throw new IllegalArgumentException("Ordinal must be between " + MIN_ORDINAL + " and " + MAX_ORDINAL);
return new CustomFlag("CUSTOM:"+ordinal, ordinal);
}
}

View File

@@ -0,0 +1,31 @@
package com.volmit.iris.util.mantle.flag;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class MantleFlagAdapter extends TypeAdapter<MantleFlag> {
private static final String CUSTOM = "CUSTOM:";
private static final int CUSTOM_LENGTH = CUSTOM.length();
@Override
public void write(JsonWriter out, MantleFlag value) throws IOException {
if (value == null) out.nullValue();
else out.value(value.toString());
}
@Override
public MantleFlag read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String s = in.nextString();
if (s.startsWith(CUSTOM) && s.length() > CUSTOM_LENGTH)
return MantleFlag.of(Integer.parseInt(s.substring(CUSTOM_LENGTH)));
return ReservedFlag.valueOf(s);
}
}

View File

@@ -0,0 +1,25 @@
package com.volmit.iris.util.mantle.flag;
public enum ReservedFlag implements MantleFlag {
OBJECT,
UPDATE,
JIGSAW,
FEATURE,
INITIAL_SPAWNED,
REAL,
CARVED,
FLUID_BODIES,
INITIAL_SPAWNED_MARKER,
CLEANED,
PLANNED,
ETCHED,
TILE,
CUSTOM,
DISCOVERED,
CUSTOM_ACTIVE;
@Override
public boolean isCustom() {
return false;
}
}

View File

@@ -0,0 +1,123 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.util.mantle.io;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
public class DelegateStream {
public static InputStream read(FileChannel channel) throws IOException {
channel.position(0);
return new Input(channel);
}
public static OutputStream write(FileChannel channel) throws IOException {
channel.position(0);
return new Output(channel);
}
private static class Input extends InputStream {
private final InputStream delegate;
private Input(FileChannel channel) {
this.delegate = Channels.newInputStream(channel);
}
@Override
public int available() throws IOException {
return delegate.available();
}
@Override
public int read() throws IOException {
return delegate.read();
}
@Override
public int read(byte @NotNull [] b, int off, int len) throws IOException {
return delegate.read(b, off, len);
}
@Override
public byte @NotNull [] readAllBytes() throws IOException {
return delegate.readAllBytes();
}
@Override
public int readNBytes(byte[] b, int off, int len) throws IOException {
return delegate.readNBytes(b, off, len);
}
@Override
public byte @NotNull [] readNBytes(int len) throws IOException {
return delegate.readNBytes(len);
}
@Override
public long skip(long n) throws IOException {
return delegate.skip(n);
}
@Override
public void skipNBytes(long n) throws IOException {
delegate.skipNBytes(n);
}
@Override
public long transferTo(OutputStream out) throws IOException {
return delegate.transferTo(out);
}
}
private static class Output extends OutputStream {
private final FileChannel channel;
private final OutputStream delegate;
private Output(FileChannel channel) {
this.channel = channel;
this.delegate = Channels.newOutputStream(channel);
}
@Override
public void write(int b) throws IOException {
delegate.write(b);
}
@Override
public void write(byte @NotNull [] b, int off, int len) throws IOException {
delegate.write(b, off, len);
}
@Override
public void flush() throws IOException {
channel.truncate(channel.position());
}
@Override
public void close() throws IOException {
channel.force(true);
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.util.mantle.io;
import com.volmit.iris.util.io.IO;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.concurrent.Semaphore;
class Holder {
private final FileChannel channel;
private final Semaphore semaphore = new Semaphore(1);
private volatile boolean closed;
Holder(FileChannel channel) throws IOException {
this.channel = channel;
IO.lock(channel);
}
SynchronizedChannel acquire() {
semaphore.acquireUninterruptibly();
if (closed) {
semaphore.release();
return null;
}
return new SynchronizedChannel(channel, semaphore);
}
void close() throws IOException {
semaphore.acquireUninterruptibly();
try {
if (closed) return;
closed = true;
channel.close();
} finally {
semaphore.release();
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.util.mantle.io;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.util.Objects;
import java.util.Set;
public class IOWorker {
private static final Set<OpenOption> OPTIONS = Set.of(StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.SYNC);
private static final int MAX_CACHE_SIZE = 128;
private final Path root;
private final File tmp;
private final int worldHeight;
private final Object2ObjectLinkedOpenHashMap<String, Holder> cache = new Object2ObjectLinkedOpenHashMap<>();
public IOWorker(File root, int worldHeight) {
this.root = root.toPath();
this.tmp = new File(root, ".tmp");
this.worldHeight = worldHeight;
}
public TectonicPlate read(final String name) throws IOException {
PrecisionStopwatch p = PrecisionStopwatch.start();
try (var channel = getChannel(name)) {
var raw = channel.read();
var lz4 = new LZ4BlockInputStream(raw);
var buffered = new BufferedInputStream(lz4);
try (var in = CountingDataInputStream.wrap(buffered)) {
return new TectonicPlate(worldHeight, in, name.startsWith("pv."));
} finally {
if (TectonicPlate.hasError() && IrisSettings.get().getGeneral().isDumpMantleOnError()) {
File dump = Iris.instance.getDataFolder("dump", name + ".bin");
Files.copy(new LZ4BlockInputStream(channel.read()), dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
} else {
Iris.debug("Read Tectonic Plate " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
}
}
}
}
public void write(final String name, final TectonicPlate plate) throws IOException {
PrecisionStopwatch p = PrecisionStopwatch.start();
try (var channel = getChannel(name)) {
tmp.mkdirs();
File file = File.createTempFile("iris", ".bin", tmp);
try {
try (var tmp = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) {
plate.write(tmp);
}
try (var out = channel.write()) {
Files.copy(file.toPath(), out);
out.flush();
}
} finally {
file.delete();
}
}
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
}
public void close() throws IOException {
synchronized (cache) {
for (Holder h : cache.values()) {
h.close();
}
cache.clear();
}
}
private SynchronizedChannel getChannel(final String name) throws IOException {
PrecisionStopwatch p = PrecisionStopwatch.start();
try {
synchronized (cache) {
Holder holder = cache.getAndMoveToFirst(name);
if (holder != null) {
var channel = holder.acquire();
if (channel != null) {
return channel;
}
}
if (cache.size() >= MAX_CACHE_SIZE) {
var last = cache.removeLast();
last.close();
}
holder = new Holder(FileChannel.open(root.resolve(name), OPTIONS));
cache.putAndMoveToFirst(name, holder);
return Objects.requireNonNull(holder.acquire());
}
} finally {
Iris.debug("Acquired Channel for " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.util.mantle.io;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.util.concurrent.Semaphore;
public class SynchronizedChannel implements Closeable {
private final FileChannel channel;
private final Semaphore lock;
private transient boolean closed;
SynchronizedChannel(FileChannel channel, Semaphore lock) {
this.channel = channel;
this.lock = lock;
}
public InputStream read() throws IOException {
if (closed) throw new IOException("Channel is closed!");
return DelegateStream.read(channel);
}
public OutputStream write() throws IOException {
if (closed) throw new IOException("Channel is closed!");
return DelegateStream.write(channel);
}
@Override
public void close() throws IOException {
if (closed) return;
closed = true;
lock.release();
}
}

View File

@@ -25,6 +25,8 @@ import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.plugin.VolmitSender;
import lombok.Getter;
import java.util.Objects;
public class IrisMatter extends IrisRegistrant implements Matter {
protected static final KMap<Class<?>, MatterSlice<?>> slicers = buildSlicers();
@@ -65,6 +67,12 @@ public class IrisMatter extends IrisRegistrant implements Matter {
return c;
}
@Override
@SuppressWarnings("unchecked")
public <T> MatterSlice<T> slice(Class<?> c) {
return (MatterSlice<T>) sliceMap.computeIfAbsent(c, $ -> Objects.requireNonNull(createSlice(c, this), "Bad slice " + c.getCanonicalName()));
}
@Override
public <T> MatterSlice<T> createSlice(Class<T> type, Matter m) {
MatterSlice<?> slice = slicers.get(type);

View File

@@ -99,10 +99,9 @@ public interface Matter {
}
static Matter read(File f) throws IOException {
FileInputStream in = new FileInputStream(f);
Matter m = read(in);
in.close();
return m;
try (var in = new FileInputStream(f)) {
return read(in);
}
}
static Matter read(InputStream in) throws IOException {
@@ -142,6 +141,7 @@ public interface Matter {
long size = din.readInt();
if (size == 0) continue;
long start = din.count();
long end = start + size;
Iris.addPanic("read.matter.slice", i + "");
try {
@@ -151,9 +151,9 @@ public interface Matter {
Class<?> type = Class.forName(cn);
MatterSlice<?> slice = matter.createSlice(type, matter);
slice.read(din);
if (din.count() < end) throw new IOException("Matter slice read size mismatch!");
matter.putSlice(type, slice);
} catch (Throwable e) {
long end = start + size;
if (!(e instanceof ClassNotFoundException)) {
Iris.error("Failed to read matter slice, skipping it.");
Iris.addPanic("read.byte.range", start + " " + end);
@@ -165,6 +165,10 @@ public interface Matter {
}
din.skipTo(end);
}
if (din.count() != end) {
throw new IOException("Matter slice read size mismatch!");
}
}
return matter;

View File

@@ -51,7 +51,7 @@ public class Bindings {
YamlConfiguration desc = resource != null ? YamlConfiguration.loadConfiguration(new InputStreamReader(resource)) : new YamlConfiguration();
Sentry.init(options -> {
options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904");
options.setDsn("http://4cdbb9ac953306529947f4ca1e8e6b26@sentry.volmit.com:8080/2");
if (settings.debug) {
options.setLogger(new IrisLogger());
options.setDebug(true);
@@ -79,7 +79,6 @@ public class Bindings {
scope.setTag("server.api", Bukkit.getBukkitVersion());
scope.setTag("iris.commit", desc.getString("commit", "unknown"));
});
Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close));
}
private static boolean suppress(Throwable e) {

Some files were not shown because too many files have changed in this diff Show More