mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-23 00:20:42 +00:00
Merge remote-tracking branch 'origin/iris4' into iris4
This commit is contained in:
11
build.gradle
11
build.gradle
@@ -65,6 +65,7 @@ def JVM_VERSION = Map.of(
|
|||||||
"v1_21_R1", 21,
|
"v1_21_R1", 21,
|
||||||
"v1_20_R4", 21,
|
"v1_20_R4", 21,
|
||||||
)
|
)
|
||||||
|
def entryPoint = 'com.volmit.iris.server.EntryPoint'
|
||||||
NMS_BINDINGS.each { nms ->
|
NMS_BINDINGS.each { nms ->
|
||||||
project(":nms:${nms.key}") {
|
project(":nms:${nms.key}") {
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
@@ -86,7 +87,6 @@ shadowJar {
|
|||||||
dependsOn(":nms:${it.key}:remap")
|
dependsOn(":nms:${it.key}:remap")
|
||||||
from("${project(":nms:${it.key}").layout.buildDirectory.asFile.get()}/libs/${it.key}-mapped.jar")
|
from("${project(":nms:${it.key}").layout.buildDirectory.asFile.get()}/libs/${it.key}-mapped.jar")
|
||||||
}
|
}
|
||||||
NMS_BINDINGS.each {dependsOn(":nms:${it.key}:build")}
|
|
||||||
//dependsOn(':com.volmit.gui:build')
|
//dependsOn(':com.volmit.gui:build')
|
||||||
|
|
||||||
//minimize()
|
//minimize()
|
||||||
@@ -95,6 +95,10 @@ shadowJar {
|
|||||||
relocate 'io.papermc.lib', 'com.volmit.iris.util.paper'
|
relocate 'io.papermc.lib', 'com.volmit.iris.util.paper'
|
||||||
relocate 'net.kyori', 'com.volmit.iris.util.kyori'
|
relocate 'net.kyori', 'com.volmit.iris.util.kyori'
|
||||||
archiveFileName.set("Iris-${project.version}.jar")
|
archiveFileName.set("Iris-${project.version}.jar")
|
||||||
|
|
||||||
|
manifest {
|
||||||
|
attributes 'Main-Class': entryPoint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -132,9 +136,9 @@ allprojects {
|
|||||||
// Shaded
|
// Shaded
|
||||||
implementation 'com.dfsek:Paralithic:0.4.0'
|
implementation 'com.dfsek:Paralithic:0.4.0'
|
||||||
implementation 'io.papermc:paperlib:1.0.5'
|
implementation 'io.papermc:paperlib:1.0.5'
|
||||||
implementation "net.kyori:adventure-text-minimessage:4.13.1"
|
implementation "net.kyori:adventure-text-minimessage:4.17.0"
|
||||||
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
||||||
implementation 'net.kyori:adventure-api:4.13.1'
|
implementation 'net.kyori:adventure-api:4.17.0'
|
||||||
//implementation 'org.bytedeco:javacpp:1.5.10'
|
//implementation 'org.bytedeco:javacpp:1.5.10'
|
||||||
//implementation 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10'
|
//implementation 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10'
|
||||||
//implementation "org.deeplearning4j:deeplearning4j-core:1.0.0-M2.1"
|
//implementation "org.deeplearning4j:deeplearning4j-core:1.0.0-M2.1"
|
||||||
@@ -156,6 +160,7 @@ allprojects {
|
|||||||
compileOnly 'net.bytebuddy:byte-buddy-agent:1.12.8'
|
compileOnly 'net.bytebuddy:byte-buddy-agent:1.12.8'
|
||||||
compileOnly 'org.bytedeco:javacpp:1.5.10'
|
compileOnly 'org.bytedeco:javacpp:1.5.10'
|
||||||
compileOnly 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10'
|
compileOnly 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10'
|
||||||
|
compileOnly 'io.netty:netty-all:4.1.112.Final'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ import com.volmit.iris.engine.object.IrisDimension;
|
|||||||
import com.volmit.iris.engine.object.IrisWorld;
|
import com.volmit.iris.engine.object.IrisWorld;
|
||||||
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
|
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
|
||||||
import com.volmit.iris.engine.platform.DummyChunkGenerator;
|
import com.volmit.iris.engine.platform.DummyChunkGenerator;
|
||||||
|
import com.volmit.iris.server.master.IrisMasterServer;
|
||||||
|
import com.volmit.iris.server.node.IrisServer;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.exceptions.IrisException;
|
import com.volmit.iris.util.exceptions.IrisException;
|
||||||
@@ -91,10 +93,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -112,6 +111,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
public static MythicMobsLink linkMythicMobs;
|
public static MythicMobsLink linkMythicMobs;
|
||||||
public static IrisCompat compat;
|
public static IrisCompat compat;
|
||||||
public static FileWatcher configWatcher;
|
public static FileWatcher configWatcher;
|
||||||
|
private static IrisServer server;
|
||||||
private static VolmitSender sender;
|
private static VolmitSender sender;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -475,6 +475,29 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
services.values().forEach(this::registerListener);
|
services.values().forEach(this::registerListener);
|
||||||
ServerConfigurator.setupDataPack();
|
ServerConfigurator.setupDataPack();
|
||||||
installMainDimension();
|
installMainDimension();
|
||||||
|
try {
|
||||||
|
info("Starting server...");
|
||||||
|
try {
|
||||||
|
int port = Integer.parseInt(System.getProperty("com.volmit.iris.server.port"));
|
||||||
|
String[] remote = Optional.ofNullable(System.getProperty("com.volmit.iris.server.remote"))
|
||||||
|
.map(String::trim)
|
||||||
|
.map(s -> s.isBlank() ? null : s.split(","))
|
||||||
|
.orElse(new String[0]);
|
||||||
|
server = remote.length > 0 ? new IrisMasterServer(port, remote) : new IrisServer(port);
|
||||||
|
} catch (NullPointerException | NumberFormatException ignored) {
|
||||||
|
var serverSettings = IrisSettings.get().getServer();
|
||||||
|
if (serverSettings.isActive()) {
|
||||||
|
server = serverSettings.isRemote() ?
|
||||||
|
new IrisMasterServer(serverSettings.getPort(), serverSettings.remote) :
|
||||||
|
new IrisServer(serverSettings.getPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
} catch (Throwable e) {
|
||||||
|
error("Failed to start server: " + e.getClass().getSimpleName());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
if (!IrisSafeguard.instance.acceptUnstable && IrisSafeguard.instance.unstablemode) {
|
if (!IrisSafeguard.instance.acceptUnstable && IrisSafeguard.instance.unstablemode) {
|
||||||
Iris.info(C.RED + "World loading has been disabled until the incompatibility is resolved.");
|
Iris.info(C.RED + "World loading has been disabled until the incompatibility is resolved.");
|
||||||
Iris.info(C.DARK_RED + "Alternatively, go to plugins/iris/settings.json and set ignoreBootMode to true.");
|
Iris.info(C.DARK_RED + "Alternatively, go to plugins/iris/settings.json and set ignoreBootMode to true.");
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class IrisSettings {
|
|||||||
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
|
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
|
||||||
private IrisWorldDump worldDump = new IrisWorldDump();
|
private IrisWorldDump worldDump = new IrisWorldDump();
|
||||||
private IrisWorldSettings irisWorldSettings = new IrisWorldSettings();
|
private IrisWorldSettings irisWorldSettings = new IrisWorldSettings();
|
||||||
|
private IrisServerSettings server = new IrisServerSettings();
|
||||||
|
|
||||||
public static int getThreadCount(int c) {
|
public static int getThreadCount(int c) {
|
||||||
return switch (c) {
|
return switch (c) {
|
||||||
@@ -180,6 +181,7 @@ public class IrisSettings {
|
|||||||
public static class IrisSettingsGUI {
|
public static class IrisSettingsGUI {
|
||||||
public boolean useServerLaunchedGuis = true;
|
public boolean useServerLaunchedGuis = true;
|
||||||
public boolean maximumPregenGuiFPS = false;
|
public boolean maximumPregenGuiFPS = false;
|
||||||
|
public boolean colorMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -202,6 +204,17 @@ public class IrisSettings {
|
|||||||
public int mcaCacheSize = 3;
|
public int mcaCacheSize = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class IrisServerSettings {
|
||||||
|
public boolean active = false;
|
||||||
|
public int port = 1337;
|
||||||
|
public String[] remote = new String[0];
|
||||||
|
|
||||||
|
public boolean isRemote() {
|
||||||
|
return remote.length != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// todo: Goal:Have these as the default world settings and when put in bukkit.yml it will again overwrite that world from these.
|
// todo: Goal:Have these as the default world settings and when put in bukkit.yml it will again overwrite that world from these.
|
||||||
@Data
|
@Data
|
||||||
public static class IrisWorldSettings {
|
public static class IrisWorldSettings {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
package com.volmit.iris.core.commands;
|
package com.volmit.iris.core.commands;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.IrisSettings;
|
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
@@ -35,8 +34,6 @@ import com.volmit.iris.util.format.C;
|
|||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||||
import com.volmit.iris.util.nbt.mca.MCAFile;
|
|
||||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||||
@@ -102,8 +99,10 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
public void packBenchmark(
|
public void packBenchmark(
|
||||||
@Param(description = "The pack to bench", aliases = {"pack"})
|
@Param(description = "The pack to bench", defaultValue = "overworld", aliases = {"pack"})
|
||||||
IrisDimension dimension,
|
IrisDimension dimension,
|
||||||
|
@Param(description = "The address to use", defaultValue = "-")
|
||||||
|
String address,
|
||||||
@Param(description = "Headless", defaultValue = "true")
|
@Param(description = "Headless", defaultValue = "true")
|
||||||
boolean headless,
|
boolean headless,
|
||||||
@Param(description = "GUI", defaultValue = "false")
|
@Param(description = "GUI", defaultValue = "false")
|
||||||
@@ -113,7 +112,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
) {
|
) {
|
||||||
int rb = diameter << 9;
|
int rb = diameter << 9;
|
||||||
Iris.info("Benchmarking pack " + dimension.getName() + " with diameter: " + rb + "(" + diameter + ")");
|
Iris.info("Benchmarking pack " + dimension.getName() + " with diameter: " + rb + "(" + diameter + ")");
|
||||||
IrisPackBenchmarking benchmark = new IrisPackBenchmarking(dimension, diameter, headless, gui);
|
IrisPackBenchmarking benchmark = new IrisPackBenchmarking(dimension, address.replace("-", "").trim(), diameter, headless, gui);
|
||||||
benchmark.runBenchmark();
|
benchmark.runBenchmark();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.volmit.iris.core.loader.IrisData;
|
|||||||
import com.volmit.iris.core.service.ObjectSVC;
|
import com.volmit.iris.core.service.ObjectSVC;
|
||||||
import com.volmit.iris.core.service.StudioSVC;
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
import com.volmit.iris.core.service.WandSVC;
|
import com.volmit.iris.core.service.WandSVC;
|
||||||
|
import com.volmit.iris.core.tools.IrisConverter;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.object.*;
|
import com.volmit.iris.engine.object.*;
|
||||||
import com.volmit.iris.util.data.Cuboid;
|
import com.volmit.iris.util.data.Cuboid;
|
||||||
@@ -211,6 +212,16 @@ public class CommandObject implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Decree(description = "Convert .schem files in the 'convert' folder to .iob files.")
|
||||||
|
public void convert () {
|
||||||
|
try {
|
||||||
|
IrisConverter.convertSchematics(sender());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Decree(description = "Get a powder that reveals objects", studio = true, aliases = "d")
|
@Decree(description = "Get a powder that reveals objects", studio = true, aliases = "d")
|
||||||
public void dust() {
|
public void dust() {
|
||||||
player().getInventory().addItem(WandSVC.createDust());
|
player().getInventory().addItem(WandSVC.createDust());
|
||||||
@@ -382,7 +393,7 @@ public class CommandObject implements DecreeExecutor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
o.write(file);
|
o.write(file, sender());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
sender().sendMessage(C.RED + "Failed to save object because of an IOException: " + e.getMessage());
|
sender().sendMessage(C.RED + "Failed to save object because of an IOException: " + e.getMessage());
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import com.volmit.iris.util.math.Spiraler;
|
|||||||
import com.volmit.iris.util.parallel.BurstExecutor;
|
import com.volmit.iris.util.parallel.BurstExecutor;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
|
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import com.volmit.iris.util.scheduling.O;
|
import com.volmit.iris.util.scheduling.O;
|
||||||
import com.volmit.iris.util.scheduling.jobs.QueueJob;
|
import com.volmit.iris.util.scheduling.jobs.QueueJob;
|
||||||
@@ -57,6 +58,7 @@ import io.papermc.lib.PaperLib;
|
|||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.event.inventory.InventoryType;
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
import org.bukkit.util.BlockVector;
|
import org.bukkit.util.BlockVector;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
@@ -71,6 +73,7 @@ import java.time.temporal.ChronoUnit;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -328,7 +331,7 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
|
|
||||||
|
|
||||||
@Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER)
|
@Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER)
|
||||||
public void distances(@Param(description = "The radius") int radius) {
|
public void distances(@Param(description = "The radius in chunks") int radius) {
|
||||||
var engine = engine();
|
var engine = engine();
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
sender().sendMessage(C.RED + "Only works in an Iris world!");
|
sender().sendMessage(C.RED + "Only works in an Iris world!");
|
||||||
@@ -342,15 +345,24 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
|
|
||||||
sender.sendMessage(C.GRAY + "Generating data...");
|
sender.sendMessage(C.GRAY + "Generating data...");
|
||||||
var loc = player().getLocation();
|
var loc = player().getLocation();
|
||||||
|
int totalTasks = d * d;
|
||||||
|
AtomicInteger completedTasks = new AtomicInteger(0);
|
||||||
|
int c = J.ar(() -> {
|
||||||
|
sender.sendProgress((double) completedTasks.get() / totalTasks, "Finding structures");
|
||||||
|
}, 0);
|
||||||
|
|
||||||
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
|
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
|
||||||
var struct = engine.getStructureAt(x, z);
|
var struct = engine.getStructureAt(x, z);
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
data.computeIfAbsent(struct.getLoadKey(), (k) -> new KList<>()).add(new Position2(x, z));
|
data.computeIfAbsent(struct.getLoadKey(), (k) -> new KList<>()).add(new Position2(x, z));
|
||||||
}
|
}
|
||||||
|
completedTasks.incrementAndGet();
|
||||||
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
|
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
|
||||||
|
|
||||||
executor.complete();
|
executor.complete();
|
||||||
multiBurst.close();
|
multiBurst.close();
|
||||||
|
J.car(c);
|
||||||
|
|
||||||
for (var key : data.keySet()) {
|
for (var key : data.keySet()) {
|
||||||
var list = data.get(key);
|
var list = data.get(key);
|
||||||
KList<Long> distances = new KList<>(list.size() - 1);
|
KList<Long> distances = new KList<>(list.size() - 1);
|
||||||
@@ -383,6 +395,7 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Decree(description = "Render a world map (External GUI)", aliases = "render")
|
@Decree(description = "Render a world map (External GUI)", aliases = "render")
|
||||||
public void map() {
|
public void map() {
|
||||||
if (noGUI()) return;
|
if (noGUI()) return;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package com.volmit.iris.core.gui;
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.events.IrisEngineHotloadEvent;
|
import com.volmit.iris.core.events.IrisEngineHotloadEvent;
|
||||||
import com.volmit.iris.engine.object.NoiseStyle;
|
import com.volmit.iris.engine.object.NoiseStyle;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -61,7 +62,7 @@ public class NoiseExplorerGUI extends JPanel implements MouseWheelListener, List
|
|||||||
@SuppressWarnings("CanBeFinal")
|
@SuppressWarnings("CanBeFinal")
|
||||||
RollingSequence r = new RollingSequence(20);
|
RollingSequence r = new RollingSequence(20);
|
||||||
@SuppressWarnings("CanBeFinal")
|
@SuppressWarnings("CanBeFinal")
|
||||||
boolean colorMode = true;
|
boolean colorMode = IrisSettings.get().getGui().colorMode;
|
||||||
double scale = 1;
|
double scale = 1;
|
||||||
CNG cng = NoiseStyle.STATIC.create(new RNG(RNG.r.nextLong()));
|
CNG cng = NoiseStyle.STATIC.create(new RNG(RNG.r.nextLong()));
|
||||||
@SuppressWarnings("CanBeFinal")
|
@SuppressWarnings("CanBeFinal")
|
||||||
@@ -274,7 +275,10 @@ public class NoiseExplorerGUI extends JPanel implements MouseWheelListener, List
|
|||||||
n = n > 1 ? 1 : n < 0 ? 0 : n;
|
n = n > 1 ? 1 : n < 0 ? 0 : n;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Color color = colorMode ? Color.getHSBColor((float) (n), 1f - (float) (n * n * n * n * n * n), 1f - (float) n) : Color.getHSBColor(0f, 0f, (float) n);
|
//Color color = colorMode ? Color.getHSBColor((float) (n), 1f - (float) (n * n * n * n * n * n), 1f - (float) n) : Color.getHSBColor(0f, 0f, (float) n);
|
||||||
|
//Color color = colorMode ? Color.getHSBColor((float) (n), (float) (n * n * n * n * n * n), (float) n) : Color.getHSBColor(0f, 0f, (float) n);
|
||||||
|
Color color = colorMode ? Color.getHSBColor((float) n, (float) (n * n * n * n * n * n), (float) n) : Color.getHSBColor(0f, 0f, (float) n);
|
||||||
|
|
||||||
int rgb = color.getRGB();
|
int rgb = color.getRGB();
|
||||||
img.setRGB(xx, z, rgb);
|
img.setRGB(xx, z, rgb);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
|
|||||||
@@ -92,7 +92,12 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
J.a(this.pregenerator::start, 20);
|
var t = new Thread(() -> {
|
||||||
|
J.sleep(1000);
|
||||||
|
this.pregenerator.start();
|
||||||
|
}, "Iris Pregenerator");
|
||||||
|
t.setPriority(Thread.MIN_PRIORITY);
|
||||||
|
t.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.volmit.iris.util.collection.KList;
|
|||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import net.Indyuce.mmoitems.MMOItems;
|
import net.Indyuce.mmoitems.MMOItems;
|
||||||
|
import net.Indyuce.mmoitems.api.ItemTier;
|
||||||
import net.Indyuce.mmoitems.api.Type;
|
import net.Indyuce.mmoitems.api.Type;
|
||||||
import net.Indyuce.mmoitems.api.block.CustomBlock;
|
import net.Indyuce.mmoitems.api.block.CustomBlock;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -66,8 +67,13 @@ public class MMOItemsDataProvider extends ExternalDataProvider {
|
|||||||
Runnable run = () -> {
|
Runnable run = () -> {
|
||||||
try {
|
try {
|
||||||
var type = api().getTypes().get(parts[1]);
|
var type = api().getTypes().get(parts[1]);
|
||||||
int level = customNbt.containsKey("level") ? (int) customNbt.get("level") : -1;
|
int level = -1;
|
||||||
var tier = api().getTiers().get(String.valueOf(customNbt.get("tier")));
|
ItemTier tier = null;
|
||||||
|
|
||||||
|
if (customNbt != null) {
|
||||||
|
level = (int) customNbt.getOrDefault("level", -1);
|
||||||
|
tier = api().getTiers().get(String.valueOf(customNbt.get("tier")));
|
||||||
|
}
|
||||||
|
|
||||||
ItemStack itemStack;
|
ItemStack itemStack;
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
|
|||||||
@@ -19,22 +19,30 @@
|
|||||||
package com.volmit.iris.core.nms;
|
package com.volmit.iris.core.nms;
|
||||||
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.server.node.IrisSession;
|
||||||
|
import com.volmit.iris.server.packet.work.ChunkPacket;
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public interface IHeadless extends Closeable {
|
public interface IHeadless extends Closeable {
|
||||||
|
|
||||||
|
void setSession(IrisSession session);
|
||||||
|
|
||||||
int getLoadedChunks();
|
int getLoadedChunks();
|
||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
boolean exists(int x, int z);
|
boolean exists(int x, int z);
|
||||||
|
|
||||||
@RegionCoordinates
|
@RegionCoordinates
|
||||||
void generateRegion(MultiBurst burst, int x, int z, PregenListener listener);
|
CompletableFuture<Void> generateRegion(MultiBurst burst, int x, int z, int maxConcurrent, PregenListener listener);
|
||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
void generateChunk(int x, int z);
|
void generateChunk(int x, int z);
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
void addChunk(ChunkPacket packet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import com.volmit.iris.util.collection.KMap;
|
|||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import com.volmit.iris.util.math.Spiraled;
|
import com.volmit.iris.util.math.Spiraled;
|
||||||
import com.volmit.iris.util.math.Spiraler;
|
import com.volmit.iris.util.math.Spiraler;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ import java.util.Comparator;
|
|||||||
|
|
||||||
@Builder
|
@Builder
|
||||||
@Data
|
@Data
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
public class PregenTask {
|
public class PregenTask {
|
||||||
private static final Position2 ZERO = new Position2(0, 0);
|
private static final Position2 ZERO = new Position2(0, 0);
|
||||||
private static final KList<Position2> ORDER_CENTER = computeChunkOrder();
|
private static final KList<Position2> ORDER_CENTER = computeChunkOrder();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.volmit.iris.Iris;
|
|||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
@@ -35,14 +34,11 @@ import org.bukkit.World;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
public class AsyncPregenMethod implements PregeneratorMethod {
|
public class AsyncPregenMethod implements PregeneratorMethod {
|
||||||
private final World world;
|
private final World world;
|
||||||
private final Engine engine;
|
|
||||||
private final MultiBurst burst;
|
private final MultiBurst burst;
|
||||||
|
|
||||||
private final KList<Future<?>> future;
|
private final KList<Future<?>> future;
|
||||||
private final Map<Chunk, Long> lastUse;
|
private final Map<Chunk, Long> lastUse;
|
||||||
|
|
||||||
@@ -51,9 +47,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
||||||
}
|
}
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.engine = IrisToolbelt.access(world).getEngine();
|
burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||||
burst = new MultiBurst("AsyncPregen", 8 );
|
future = new KList<>(256);
|
||||||
future = new KList<>(1024);
|
|
||||||
this.lastUse = new KMap<>();
|
this.lastUse = new KMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,22 +76,18 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
|
|
||||||
private void completeChunk(int x, int z, PregenListener listener) {
|
private void completeChunk(int x, int z, PregenListener listener) {
|
||||||
try {
|
try {
|
||||||
future.add(PaperLib.getChunkAtAsync(world, x, z, true).thenApply((i) -> {
|
PaperLib.getChunkAtAsync(world, x, z, true).thenAccept((i) -> {
|
||||||
if (i == null) return 0;
|
|
||||||
lastUse.put(i, M.ms());
|
lastUse.put(i, M.ms());
|
||||||
listener.onChunkGenerated(x, z);
|
listener.onChunkGenerated(x, z);
|
||||||
listener.onChunkCleaned(x, z);
|
listener.onChunkCleaned(x, z);
|
||||||
return 0;
|
}).get();
|
||||||
}));
|
} catch (InterruptedException ignored) {
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForChunksPartial(int maxWaiting) {
|
private void waitForChunksPartial(int maxWaiting) {
|
||||||
future.removeWhere(Objects::isNull);
|
|
||||||
|
|
||||||
while (future.size() > maxWaiting) {
|
while (future.size() > maxWaiting) {
|
||||||
try {
|
try {
|
||||||
Future<?> i = future.remove(0);
|
Future<?> i = future.remove(0);
|
||||||
@@ -125,8 +116,6 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
future.removeWhere(Objects::isNull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -143,6 +132,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
public void close() {
|
public void close() {
|
||||||
waitForChunks();
|
waitForChunks();
|
||||||
unloadAndSaveAllChunks();
|
unloadAndSaveAllChunks();
|
||||||
|
burst.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public class HeadlessPregenMethod implements PregeneratorMethod {
|
|||||||
Iris.error("Failed to close headless");
|
Iris.error("Failed to close headless");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
burst.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import com.volmit.iris.util.plugin.IrisService;
|
|||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import com.volmit.iris.util.scheduling.SR;
|
import com.volmit.iris.util.scheduling.SR;
|
||||||
|
import com.volmit.iris.util.scheduling.jobs.Job;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.enchantments.Enchantment;
|
import org.bukkit.enchantments.Enchantment;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@@ -57,8 +57,8 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
|
|
||||||
public class WandSVC implements IrisService {
|
public class WandSVC implements IrisService {
|
||||||
private static final Particle CRIT_MAGIC = E.getOrDefault(Particle.class, "CRIT_MAGIC", "CRIT");
|
private static final Particle CRIT_MAGIC = E.getOrDefault(Particle.class, "CRIT_MAGIC", "CRIT");
|
||||||
private static final Particle REDSTONE = E.getOrDefault(Particle.class, "REDSTONE", "DUST");
|
private static final Particle REDSTONE = E.getOrDefault(Particle.class, "REDSTONE", "DUST");
|
||||||
private static final int BLOCKS_PER_TICK = Integer.parseInt(System.getProperty("iris.blocks_per_tick", "1000"));
|
private static final int MS_PER_TICK = Integer.parseInt(System.getProperty("iris.ms_per_tick", "30"));
|
||||||
|
|
||||||
private static ItemStack dust;
|
private static ItemStack dust;
|
||||||
private static ItemStack wand;
|
private static ItemStack wand;
|
||||||
@@ -83,28 +83,80 @@ public class WandSVC implements IrisService {
|
|||||||
Cuboid c = new Cuboid(f[0], f[1]);
|
Cuboid c = new Cuboid(f[0], f[1]);
|
||||||
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ());
|
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ());
|
||||||
|
|
||||||
var it = c.iterator();
|
var it = c.chunkedIterator();
|
||||||
|
|
||||||
|
int total = c.getSizeX() * c.getSizeY() * c.getSizeZ();
|
||||||
var latch = new CountDownLatch(1);
|
var latch = new CountDownLatch(1);
|
||||||
new SR() {
|
new Job() {
|
||||||
|
private int i;
|
||||||
|
private Chunk chunk;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public String getName() {
|
||||||
for (int i = 0; i < BLOCKS_PER_TICK; i++) {
|
return "Scanning Selection";
|
||||||
if (!it.hasNext()) {
|
|
||||||
cancel();
|
|
||||||
latch.countDown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var b = it.next();
|
|
||||||
if (b.getType().equals(Material.AIR))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
BlockVector bv = b.getLocation().subtract(c.getLowerNE().toVector()).toVector().toBlockVector();
|
|
||||||
s.setUnsigned(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ(), b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
latch.await();
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
new SR() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
var time = M.ms() + MS_PER_TICK;
|
||||||
|
while (time > M.ms()) {
|
||||||
|
if (!it.hasNext()) {
|
||||||
|
if (chunk != null) {
|
||||||
|
chunk.removePluginChunkTicket(Iris.instance);
|
||||||
|
chunk = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel();
|
||||||
|
latch.countDown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var b = it.next();
|
||||||
|
var bChunk = b.getChunk();
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = bChunk;
|
||||||
|
chunk.addPluginChunkTicket(Iris.instance);
|
||||||
|
} else if (chunk != bChunk) {
|
||||||
|
chunk.removePluginChunkTicket(Iris.instance);
|
||||||
|
chunk = bChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.getType().equals(Material.AIR))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BlockVector bv = b.getLocation().subtract(c.getLowerNE().toVector()).toVector().toBlockVector();
|
||||||
|
s.setUnsigned(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ(), b);
|
||||||
|
} finally {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completeWork() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalWork() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWorkCompleted() {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}.execute(new VolmitSender(p), true, () -> {});
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
package com.volmit.iris.core.tools;
|
package com.volmit.iris.core.tools;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.engine.object.IrisObject;
|
import com.volmit.iris.engine.object.*;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
import com.volmit.iris.util.nbt.io.NBTUtil;
|
||||||
@@ -28,15 +28,14 @@ import com.volmit.iris.util.nbt.tag.*;
|
|||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -48,11 +47,15 @@ public class IrisConverter {
|
|||||||
|
|
||||||
FilenameFilter filter = (dir, name) -> name.endsWith(".schem");
|
FilenameFilter filter = (dir, name) -> name.endsWith(".schem");
|
||||||
File[] fileList = folder.listFiles(filter);
|
File[] fileList = folder.listFiles(filter);
|
||||||
|
if (fileList == null) {
|
||||||
|
sender.sendMessage("No schematic files to convert found in " + folder.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(1);
|
ExecutorService executorService = Executors.newFixedThreadPool(1);
|
||||||
executorService.submit(() -> {
|
executorService.submit(() -> {
|
||||||
for (File schem : fileList) {
|
for (File schem : fileList) {
|
||||||
try {
|
try {
|
||||||
PrecisionStopwatch p = new PrecisionStopwatch();
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
boolean largeObject = false;
|
boolean largeObject = false;
|
||||||
NamedTag tag = null;
|
NamedTag tag = null;
|
||||||
try {
|
try {
|
||||||
@@ -63,346 +66,84 @@ public class IrisConverter {
|
|||||||
}
|
}
|
||||||
CompoundTag compound = (CompoundTag) tag.getTag();
|
CompoundTag compound = (CompoundTag) tag.getTag();
|
||||||
|
|
||||||
if (compound.containsKey("Palette") && compound.containsKey("Width") && compound.containsKey("Height") && compound.containsKey("Length")) {
|
if (compound.containsKey("Palette") && compound.containsKey("Width") && compound.containsKey("Height") && compound.containsKey("Length")) {
|
||||||
int objW = ((ShortTag) compound.get("Width")).getValue();
|
int objW = ((ShortTag) compound.get("Width")).getValue();
|
||||||
int objH = ((ShortTag) compound.get("Height")).getValue();
|
int objH = ((ShortTag) compound.get("Height")).getValue();
|
||||||
int objD = ((ShortTag) compound.get("Length")).getValue();
|
int objD = ((ShortTag) compound.get("Length")).getValue();
|
||||||
int mv = objW * objH * objD;
|
int i = -1;
|
||||||
AtomicInteger v = new AtomicInteger(0);
|
int mv = objW * objH * objD;
|
||||||
AtomicInteger fv = new AtomicInteger(0);
|
AtomicInteger v = new AtomicInteger(0);
|
||||||
if (mv > 500_000) {
|
if (mv > 500_000) {
|
||||||
largeObject = true;
|
largeObject = true;
|
||||||
Iris.info(C.GRAY + "Converting.. " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
|
Iris.info(C.GRAY + "Converting.. "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
|
||||||
Iris.info(C.GRAY + "- It may take a while");
|
Iris.info(C.GRAY + "- It may take a while");
|
||||||
if (sender.isPlayer()) {
|
|
||||||
J.a(() -> {
|
|
||||||
// while (v.get() != mv) {
|
|
||||||
// double pr = ((double) v.get() / (double ) mv);
|
|
||||||
// sender.sendProgress(pr, "Converting");
|
|
||||||
// J.sleep(16);
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CompoundTag paletteTag = (CompoundTag) compound.get("Palette");
|
|
||||||
Map<Integer, BlockData> blockmap = new HashMap<>(paletteTag.size(), 0.9f);
|
|
||||||
for (Map.Entry<String, Tag<?>> entry : paletteTag.getValue().entrySet()) {
|
|
||||||
String blockName = entry.getKey();
|
|
||||||
BlockData bd = Bukkit.createBlockData(blockName);
|
|
||||||
Tag<?> blockTag = entry.getValue();
|
|
||||||
int blockId = ((IntTag) blockTag).getValue();
|
|
||||||
blockmap.put(blockId, bd);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayTag byteArray = (ByteArrayTag) compound.get("BlockData");
|
|
||||||
byte[] originalBlockArray = byteArray.getValue();
|
|
||||||
int b = 0;
|
|
||||||
int a = 0;
|
|
||||||
Map<Integer, Byte> y = new HashMap<>();
|
|
||||||
Map<Integer, Byte> x = new HashMap<>();
|
|
||||||
Map<Integer, Byte> z = new HashMap<>();
|
|
||||||
|
|
||||||
// Height adjustments
|
|
||||||
for (int h = 0; h < objH; h++) {
|
|
||||||
if (b == 0) {
|
|
||||||
y.put(h, (byte) 0);
|
|
||||||
}
|
|
||||||
if (b > 0) {
|
|
||||||
y.put(h, (byte) 1);
|
|
||||||
}
|
|
||||||
a = 0;
|
|
||||||
b = 0;
|
|
||||||
for (int d = 0; d < objD; d++) {
|
|
||||||
for (int w = 0; w < objW; w++) {
|
|
||||||
BlockData db = blockmap.get((int) originalBlockArray[fv.get()]);
|
|
||||||
if (db.getAsString().contains("minecraft:air")) {
|
|
||||||
a++;
|
|
||||||
} else {
|
|
||||||
b++;
|
|
||||||
}
|
|
||||||
fv.getAndAdd(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fv.set(0);
|
|
||||||
|
|
||||||
// Width adjustments
|
|
||||||
for (int w = 0; w < objW; w++) {
|
|
||||||
if (b == 0) {
|
|
||||||
x.put(w, (byte) 0);
|
|
||||||
}
|
|
||||||
if (b > 0) {
|
|
||||||
x.put(w, (byte) 1);
|
|
||||||
}
|
|
||||||
a = 0;
|
|
||||||
b = 0;
|
|
||||||
for (int h = 0; h < objH; h++) {
|
|
||||||
for (int d = 0; d < objD; d++) {
|
|
||||||
BlockData db = blockmap.get((int) originalBlockArray[fv.get()]);
|
|
||||||
if (db.getAsString().contains("minecraft:air")) {
|
|
||||||
a++;
|
|
||||||
} else {
|
|
||||||
b++;
|
|
||||||
}
|
|
||||||
fv.getAndAdd(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fv.set(0);
|
|
||||||
|
|
||||||
// Depth adjustments
|
|
||||||
for (int d = 0; d < objD; d++) {
|
|
||||||
if (b == 0) {
|
|
||||||
z.put(d, (byte) 0);
|
|
||||||
}
|
|
||||||
if (b > 0) {
|
|
||||||
z.put(d, (byte) 1);
|
|
||||||
}
|
|
||||||
a = 0;
|
|
||||||
b = 0;
|
|
||||||
for (int h = 0; h < objH; h++) {
|
|
||||||
for (int w = 0; w < objW; w++) {
|
|
||||||
BlockData db = blockmap.get((int) originalBlockArray[fv.get()]);
|
|
||||||
if (db.getAsString().contains("minecraft:air")) {
|
|
||||||
a++;
|
|
||||||
} else {
|
|
||||||
b++;
|
|
||||||
}
|
|
||||||
fv.getAndAdd(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fv.set(0);
|
|
||||||
int CorrectObjH = getCorrectY(y, objH);
|
|
||||||
int CorrectObjW = getCorrectX(x, objW);
|
|
||||||
int CorrectObjD = getCorrectZ(z, objD);
|
|
||||||
|
|
||||||
//IrisObject object = new IrisObject(CorrectObjW, CorrectObjH, CorrectObjH);
|
|
||||||
IrisObject object = new IrisObject(objW, objH, objD);
|
|
||||||
Vector originalVector = new Vector(objW, objH, objD);
|
|
||||||
|
|
||||||
|
|
||||||
int[] yc = null;
|
|
||||||
int[] xc = null;
|
|
||||||
int[] zc = null;
|
|
||||||
|
|
||||||
|
|
||||||
int fo = 0;
|
|
||||||
int so = 0;
|
|
||||||
int o = 0;
|
|
||||||
int c = 0;
|
|
||||||
for (Integer i : y.keySet()) {
|
|
||||||
if (y.get(i) == 0) {
|
|
||||||
o++;
|
|
||||||
}
|
|
||||||
if (y.get(i) == 1) {
|
|
||||||
c++;
|
|
||||||
if (c == 1) {
|
|
||||||
fo = o;
|
|
||||||
}
|
|
||||||
o = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
so = o;
|
|
||||||
yc = new int[]{fo, so};
|
|
||||||
|
|
||||||
fo = 0;
|
|
||||||
so = 0;
|
|
||||||
o = 0;
|
|
||||||
c = 0;
|
|
||||||
for (Integer i : x.keySet()) {
|
|
||||||
if (x.get(i) == 0) {
|
|
||||||
o++;
|
|
||||||
}
|
|
||||||
if (x.get(i) == 1) {
|
|
||||||
c++;
|
|
||||||
if (c == 1) {
|
|
||||||
fo = o;
|
|
||||||
}
|
|
||||||
o = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
so = o;
|
|
||||||
xc = new int[]{fo, so};
|
|
||||||
|
|
||||||
fo = 0;
|
|
||||||
so = 0;
|
|
||||||
o = 0;
|
|
||||||
c = 0;
|
|
||||||
for (Integer i : z.keySet()) {
|
|
||||||
if (z.get(i) == 0) {
|
|
||||||
o++;
|
|
||||||
}
|
|
||||||
if (z.get(i) == 1) {
|
|
||||||
c++;
|
|
||||||
if (c == 1) {
|
|
||||||
fo = o;
|
|
||||||
}
|
|
||||||
o = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
so = o;
|
|
||||||
zc = new int[]{fo, so};
|
|
||||||
|
|
||||||
int h1, h2, w1, w2, v1 = 0, volume = objW * objH * objD;
|
|
||||||
Map<Integer, Integer> blockLocationMap = new LinkedHashMap<>();
|
|
||||||
boolean hasAir = false;
|
|
||||||
int pos = 0;
|
|
||||||
for (int i : originalBlockArray) {
|
|
||||||
blockLocationMap.put(pos, i);
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (int h = 0; h < objH; h++) {
|
|
||||||
for (int d = 0; d < objD; d++) {
|
|
||||||
for (int w = 0; w < objW; w++) {
|
|
||||||
BlockData bd = blockmap.get((int) originalBlockArray[v.get()]);
|
|
||||||
if (!bd.getMaterial().isAir()) {
|
|
||||||
object.setUnsigned(w, h, d, bd);
|
|
||||||
}
|
|
||||||
v.getAndAdd(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
object.write(new File(folder, schem.getName().replace(".schem", ".iob")));
|
|
||||||
} catch (IOException e) {
|
|
||||||
Iris.info(C.RED + "Failed to save: " + schem.getName());
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
if (sender.isPlayer()) {
|
if (sender.isPlayer()) {
|
||||||
if (largeObject) {
|
i = J.ar(() -> {
|
||||||
sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
|
sender.sendProgress((double) v.get() / mv, "Converting");
|
||||||
} else {
|
}, 0);
|
||||||
sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag paletteTag = (CompoundTag) compound.get("Palette");
|
||||||
|
Map<Integer, BlockData> blockmap = new HashMap<>(paletteTag.size(), 0.9f);
|
||||||
|
for (Map.Entry<String, Tag<?>> entry : paletteTag.getValue().entrySet()) {
|
||||||
|
String blockName = entry.getKey();
|
||||||
|
BlockData bd = Bukkit.createBlockData(blockName);
|
||||||
|
Tag<?> blockTag = entry.getValue();
|
||||||
|
int blockId = ((IntTag) blockTag).getValue();
|
||||||
|
blockmap.put(blockId, bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayTag byteArray = (ByteArrayTag) compound.get("BlockData");
|
||||||
|
byte[] originalBlockArray = byteArray.getValue();
|
||||||
|
|
||||||
|
IrisObject object = new IrisObject(objW, objH, objD);
|
||||||
|
for (int h = 0; h < objH; h++) {
|
||||||
|
for (int d = 0; d < objD; d++) {
|
||||||
|
for (int w = 0; w < objW; w++) {
|
||||||
|
BlockData bd = blockmap.get((int) originalBlockArray[v.get()]);
|
||||||
|
if (!bd.getMaterial().isAir()) {
|
||||||
|
object.setUnsigned(w, h, d, bd);
|
||||||
|
}
|
||||||
|
v.getAndAdd(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (largeObject) {
|
|
||||||
Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
|
|
||||||
} else {
|
|
||||||
Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
|
|
||||||
}
|
|
||||||
// schem.delete();
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
if (i != -1) J.car(i);
|
||||||
Iris.info(C.RED + "Failed to convert: " + schem.getName());
|
try {
|
||||||
|
object.shrinkwrap();
|
||||||
|
object.write(new File(folder, schem.getName().replace(".schem", ".iob")));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.info(C.RED + "Failed to save: " + schem.getName());
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
if (sender.isPlayer()) {
|
if (sender.isPlayer()) {
|
||||||
sender.sendMessage(C.RED + "Failed to convert: " + schem.getName());
|
if (largeObject) {
|
||||||
|
sender.sendMessage(C.IRIS + "Converted "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(C.IRIS + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e.printStackTrace();
|
if (largeObject) {
|
||||||
Iris.reportError(e);
|
Iris.info(C.GRAY + "Converted "+ schem.getName() + " -> " + schem.getName().replace(".schem", ".iob") + " in " + Form.duration(p.getMillis()));
|
||||||
|
} else {
|
||||||
|
Iris.info(C.GRAY + "Converted " + schem.getName() + " -> " + schem.getName().replace(".schem", ".iob"));
|
||||||
|
}
|
||||||
|
FileUtils.delete(schem);
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Iris.info(C.RED + "Failed to convert: " + schem.getName());
|
||||||
|
if (sender.isPlayer()) {
|
||||||
|
sender.sendMessage(C.RED + "Failed to convert: " + schem.getName());
|
||||||
|
}
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sender.sendMessage(C.GRAY + "converted: " + fileList.length);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNewPointFurther(int[] originalPoint, int[] oldPoint, int[] newPoint) {
|
|
||||||
int oX = oldPoint[1];
|
|
||||||
int oY = oldPoint[2];
|
|
||||||
int oZ = oldPoint[3];
|
|
||||||
|
|
||||||
int nX = newPoint[1];
|
|
||||||
int nY = newPoint[2];
|
|
||||||
int nZ = newPoint[3];
|
|
||||||
|
|
||||||
int orX = originalPoint[1];
|
|
||||||
int orY = originalPoint[2];
|
|
||||||
int orZ = originalPoint[3];
|
|
||||||
|
|
||||||
double oldDistance = Math.sqrt(Math.pow(oX - orX, 2) + Math.pow(oY - orY, 2) + Math.pow(oZ - orZ, 2));
|
|
||||||
double newDistance = Math.sqrt(Math.pow(nX - orX, 2) + Math.pow(nY - orY, 2) + Math.pow(nZ - orZ, 2));
|
|
||||||
|
|
||||||
if (newDistance > oldDistance) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int[] getCoordinates(int pos, int obX, int obY, int obZ) {
|
|
||||||
int z = 0;
|
|
||||||
int[] coords = new int[4];
|
|
||||||
for (int h = 0; h < obY; h++) {
|
|
||||||
for (int d = 0; d < obZ; d++) {
|
|
||||||
for (int w = 0; w < obX; w++) {
|
|
||||||
if (z == pos) {
|
|
||||||
coords[1] = w;
|
|
||||||
coords[2] = h;
|
|
||||||
coords[3] = d;
|
|
||||||
return coords;
|
|
||||||
}
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getCorrectY(Map<Integer, Byte> y, int H) {
|
|
||||||
int fo = 0;
|
|
||||||
int so = 0;
|
|
||||||
int o = 0;
|
|
||||||
int c = 0;
|
|
||||||
for (Integer i : y.keySet()) {
|
|
||||||
if (y.get(i) == 0) {
|
|
||||||
o++;
|
|
||||||
}
|
|
||||||
if (y.get(i) == 1) {
|
|
||||||
c++;
|
|
||||||
if (c == 1) {
|
|
||||||
fo = o;
|
|
||||||
}
|
|
||||||
o = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
so = o;
|
|
||||||
return H = H - (fo + so);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getCorrectX(Map<Integer, Byte> x, int W) {
|
|
||||||
int fo = 0;
|
|
||||||
int so = 0;
|
|
||||||
int o = 0;
|
|
||||||
int c = 0;
|
|
||||||
for (Integer i : x.keySet()) {
|
|
||||||
if (x.get(i) == 0) {
|
|
||||||
o++;
|
|
||||||
}
|
|
||||||
if (x.get(i) == 1) {
|
|
||||||
c++;
|
|
||||||
if (c == 1) {
|
|
||||||
fo = o;
|
|
||||||
}
|
|
||||||
o = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
so = o;
|
|
||||||
return W = W - (fo + so);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getCorrectZ(Map<Integer, Byte> z, int D) {
|
|
||||||
int fo = 0;
|
|
||||||
int so = 0;
|
|
||||||
int o = 0;
|
|
||||||
int c = 0;
|
|
||||||
for (Integer i : z.keySet()) {
|
|
||||||
if (z.get(i) == 0) {
|
|
||||||
o++;
|
|
||||||
}
|
|
||||||
if (z.get(i) == 1) {
|
|
||||||
c++;
|
|
||||||
if (c == 1) {
|
|
||||||
fo = o;
|
|
||||||
}
|
|
||||||
o = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
so = o;
|
|
||||||
return D = D - (fo + so);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import com.volmit.iris.engine.framework.Engine;
|
|||||||
import com.volmit.iris.engine.framework.EngineTarget;
|
import com.volmit.iris.engine.framework.EngineTarget;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
import com.volmit.iris.engine.object.IrisWorld;
|
import com.volmit.iris.engine.object.IrisWorld;
|
||||||
|
import com.volmit.iris.server.pregen.CloudMethod;
|
||||||
|
import com.volmit.iris.server.pregen.CloudTask;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.exceptions.IrisException;
|
import com.volmit.iris.util.exceptions.IrisException;
|
||||||
@@ -65,10 +67,12 @@ public class IrisPackBenchmarking {
|
|||||||
private int radius;
|
private int radius;
|
||||||
private boolean finished = false;
|
private boolean finished = false;
|
||||||
private Engine engine;
|
private Engine engine;
|
||||||
|
private String address;
|
||||||
|
|
||||||
public IrisPackBenchmarking(IrisDimension dimension, int r, boolean headless, boolean gui) {
|
public IrisPackBenchmarking(IrisDimension dimension, String address, int r, boolean headless, boolean gui) {
|
||||||
instance = this;
|
instance = this;
|
||||||
this.IrisDimension = dimension;
|
this.IrisDimension = dimension;
|
||||||
|
this.address = address;
|
||||||
this.radius = r;
|
this.radius = r;
|
||||||
this.headless = headless;
|
this.headless = headless;
|
||||||
this.gui = gui;
|
this.gui = gui;
|
||||||
@@ -90,7 +94,13 @@ public class IrisPackBenchmarking {
|
|||||||
}
|
}
|
||||||
Iris.info("Starting Benchmark!");
|
Iris.info("Starting Benchmark!");
|
||||||
stopwatch.begin();
|
stopwatch.begin();
|
||||||
startBenchmark();
|
try {
|
||||||
|
if (address != null && !address.isBlank())
|
||||||
|
startCloudBenchmark();
|
||||||
|
else startBenchmark();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}, "PackBenchmarking").start();
|
}, "PackBenchmarking").start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +207,19 @@ public class IrisPackBenchmarking {
|
|||||||
IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), engine);
|
IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism())), engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startCloudBenchmark() throws InterruptedException {
|
||||||
|
int x = 0;
|
||||||
|
int z = 0;
|
||||||
|
IrisToolbelt.pregenerate(CloudTask
|
||||||
|
.couldBuilder()
|
||||||
|
.gui(gui)
|
||||||
|
.center(new Position2(x, z))
|
||||||
|
.width(radius)
|
||||||
|
.height(radius)
|
||||||
|
.distance(engine.getMantle().getRadius() * 2)
|
||||||
|
.build(), new CloudMethod(address, engine), engine);
|
||||||
|
}
|
||||||
|
|
||||||
private double calculateAverage(KList<Integer> list) {
|
private double calculateAverage(KList<Integer> list) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int num : list) {
|
for (int num : list) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class WandSelection {
|
|||||||
private static final Particle REDSTONE = E.getOrDefault(Particle.class, "REDSTONE", "DUST");
|
private static final Particle REDSTONE = E.getOrDefault(Particle.class, "REDSTONE", "DUST");
|
||||||
private final Cuboid c;
|
private final Cuboid c;
|
||||||
private final Player p;
|
private final Player p;
|
||||||
|
private static final double STEP = 0.10;
|
||||||
|
|
||||||
public WandSelection(Cuboid c, Player p) {
|
public WandSelection(Cuboid c, Player p) {
|
||||||
this.c = c;
|
this.c = c;
|
||||||
@@ -40,77 +41,58 @@ public class WandSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void draw() {
|
public void draw() {
|
||||||
double accuracy;
|
Location playerLoc = p.getLocation();
|
||||||
double dist;
|
double maxDistanceSquared = 256 * 256;
|
||||||
|
int particleCount = 0;
|
||||||
|
|
||||||
for (double i = c.getLowerX() - 1; i < c.getUpperX() + 1; i += 0.25) {
|
// cube!
|
||||||
for (double j = c.getLowerY() - 1; j < c.getUpperY() + 1; j += 0.25) {
|
Location[][] edges = {
|
||||||
for (double k = c.getLowerZ() - 1; k < c.getUpperZ() + 1; k += 0.25) {
|
{c.getLowerNE(), new Location(c.getWorld(), c.getUpperX() + 1, c.getLowerY(), c.getLowerZ())},
|
||||||
boolean ii = i == c.getLowerX() || i == c.getUpperX();
|
{c.getLowerNE(), new Location(c.getWorld(), c.getLowerX(), c.getUpperY() + 1, c.getLowerZ())},
|
||||||
boolean jj = j == c.getLowerY() || j == c.getUpperY();
|
{c.getLowerNE(), new Location(c.getWorld(), c.getLowerX(), c.getLowerY(), c.getUpperZ() + 1)},
|
||||||
boolean kk = k == c.getLowerZ() || k == c.getUpperZ();
|
{new Location(c.getWorld(), c.getUpperX() + 1, c.getLowerY(), c.getLowerZ()), new Location(c.getWorld(), c.getUpperX() + 1, c.getUpperY() + 1, c.getLowerZ())},
|
||||||
|
{new Location(c.getWorld(), c.getUpperX() + 1, c.getLowerY(), c.getLowerZ()), new Location(c.getWorld(), c.getUpperX() + 1, c.getLowerY(), c.getUpperZ() + 1)},
|
||||||
|
{new Location(c.getWorld(), c.getLowerX(), c.getUpperY() + 1, c.getLowerZ()), new Location(c.getWorld(), c.getUpperX() + 1, c.getUpperY() + 1, c.getLowerZ())},
|
||||||
|
{new Location(c.getWorld(), c.getLowerX(), c.getUpperY() + 1, c.getLowerZ()), new Location(c.getWorld(), c.getLowerX(), c.getUpperY() + 1, c.getUpperZ() + 1)},
|
||||||
|
{new Location(c.getWorld(), c.getLowerX(), c.getLowerY(), c.getUpperZ() + 1), new Location(c.getWorld(), c.getUpperX() + 1, c.getLowerY(), c.getUpperZ() + 1)},
|
||||||
|
{new Location(c.getWorld(), c.getLowerX(), c.getLowerY(), c.getUpperZ() + 1), new Location(c.getWorld(), c.getLowerX(), c.getUpperY() + 1, c.getUpperZ() + 1)},
|
||||||
|
{new Location(c.getWorld(), c.getUpperX() + 1, c.getUpperY() + 1, c.getLowerZ()), new Location(c.getWorld(), c.getUpperX() + 1, c.getUpperY() + 1, c.getUpperZ() + 1)},
|
||||||
|
{new Location(c.getWorld(), c.getLowerX(), c.getUpperY() + 1, c.getUpperZ() + 1), new Location(c.getWorld(), c.getUpperX() + 1, c.getUpperY() + 1, c.getUpperZ() + 1)},
|
||||||
|
{new Location(c.getWorld(), c.getUpperX() + 1, c.getLowerY(), c.getUpperZ() + 1), new Location(c.getWorld(), c.getUpperX() + 1, c.getUpperY() + 1, c.getUpperZ() + 1)}
|
||||||
|
};
|
||||||
|
|
||||||
if ((ii && jj) || (ii && kk) || (kk && jj)) {
|
for (Location[] edge : edges) {
|
||||||
Vector push = new Vector(0, 0, 0);
|
Vector direction = edge[1].toVector().subtract(edge[0].toVector());
|
||||||
|
double length = direction.length();
|
||||||
|
direction.normalize();
|
||||||
|
|
||||||
if (i == c.getLowerX()) {
|
for (double d = 0; d <= length; d += STEP) {
|
||||||
push.add(new Vector(-0.55, 0, 0));
|
Location particleLoc = edge[0].clone().add(direction.clone().multiply(d));
|
||||||
}
|
|
||||||
|
|
||||||
if (j == c.getLowerY()) {
|
if (playerLoc.distanceSquared(particleLoc) > maxDistanceSquared) {
|
||||||
push.add(new Vector(0, -0.55, 0));
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (k == c.getLowerZ()) {
|
|
||||||
push.add(new Vector(0, 0, -0.55));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == c.getUpperX()) {
|
|
||||||
push.add(new Vector(0.55, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j == c.getUpperY()) {
|
|
||||||
push.add(new Vector(0, 0.55, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k == c.getUpperZ()) {
|
|
||||||
push.add(new Vector(0, 0, 0.55));
|
|
||||||
}
|
|
||||||
|
|
||||||
Location a = new Location(c.getWorld(), i, j, k).add(0.5, 0.5, 0.5).add(push);
|
|
||||||
accuracy = M.lerpInverse(0, 64 * 64, p.getLocation().distanceSquared(a));
|
|
||||||
dist = M.lerp(0.125, 3.5, accuracy);
|
|
||||||
|
|
||||||
if (M.r(M.min(dist * 5, 0.9D) * 0.995)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ii && jj) {
|
|
||||||
a.add(0, 0, RNG.r.d(-0.3, 0.3));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kk && jj) {
|
|
||||||
a.add(RNG.r.d(-0.3, 0.3), 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ii && kk) {
|
|
||||||
a.add(0, RNG.r.d(-0.3, 0.3), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.getLocation().distanceSquared(a) < 256 * 256) {
|
|
||||||
Color color = Color.getHSBColor((float) (0.5f + (Math.sin((i + j + k + (p.getTicksLived() / 2f)) / (20f)) / 2)), 1, 1);
|
|
||||||
int r = color.getRed();
|
|
||||||
int g = color.getGreen();
|
|
||||||
int b = color.getBlue();
|
|
||||||
|
|
||||||
p.spawnParticle(REDSTONE, a.getX(), a.getY(), a.getZ(),
|
|
||||||
1, 0, 0, 0, 0,
|
|
||||||
new Particle.DustOptions(org.bukkit.Color.fromRGB(r, g, b),
|
|
||||||
(float) dist * 3f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spawnParticle(particleLoc, playerLoc);
|
||||||
|
particleCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void spawnParticle(Location particleLoc, Location playerLoc) {
|
||||||
|
double accuracy = M.lerpInverse(0, 64 * 64, playerLoc.distanceSquared(particleLoc));
|
||||||
|
double dist = M.lerp(0.125, 3.5, accuracy);
|
||||||
|
|
||||||
|
if (M.r(Math.min(dist * 5, 0.9D) * 0.995)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float hue = (float) (0.5f + (Math.sin((particleLoc.getX() + particleLoc.getY() + particleLoc.getZ() + (p.getTicksLived() / 2f)) / 20f) / 2));
|
||||||
|
Color color = Color.getHSBColor(hue, 1, 1);
|
||||||
|
|
||||||
|
p.spawnParticle(REDSTONE, particleLoc,
|
||||||
|
0, 0, 0, 0, 1,
|
||||||
|
new Particle.DustOptions(org.bukkit.Color.fromRGB(color.getRed(), color.getGreen(), color.getBlue()),
|
||||||
|
(float) dist * 3f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import com.volmit.iris.util.math.Position2;
|
|||||||
import com.volmit.iris.util.math.RNG;
|
import com.volmit.iris.util.math.RNG;
|
||||||
import com.volmit.iris.util.matter.MatterCavern;
|
import com.volmit.iris.util.matter.MatterCavern;
|
||||||
import com.volmit.iris.util.matter.MatterUpdate;
|
import com.volmit.iris.util.matter.MatterUpdate;
|
||||||
|
import com.volmit.iris.util.matter.TileWrapper;
|
||||||
import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer;
|
import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer;
|
||||||
import com.volmit.iris.util.parallel.BurstExecutor;
|
import com.volmit.iris.util.parallel.BurstExecutor;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
@@ -283,9 +284,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
|
|
||||||
var mc = getMantle().getMantle().getChunk(c.getX(), c.getZ());
|
var mc = getMantle().getMantle().getChunk(c.getX(), c.getZ());
|
||||||
mc.raiseFlag(MantleFlag.TILE, () -> J.s(() -> {
|
mc.raiseFlag(MantleFlag.TILE, () -> J.s(() -> {
|
||||||
mc.iterate(TileData.class, (x, y, z, tile) -> {
|
mc.iterate(TileWrapper.class, (x, y, z, tile) -> {
|
||||||
int betterY = y + getWorld().minHeight();
|
int betterY = y + getWorld().minHeight();
|
||||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), tile))
|
if (!TileData.setTileState(c.getBlock(x, betterY, z), tile.getData()))
|
||||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), tile.getMaterial().name());
|
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), tile.getMaterial().name());
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) {
|
private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) {
|
||||||
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
|
long seed = cng.fit(Integer.MIN_VALUE, Integer.MAX_VALUE, x, z);
|
||||||
|
|
||||||
if (getDimension().getStronghold() != null) {
|
if (getDimension().getStronghold() != null) {
|
||||||
List<Position2> poss = getDimension().getStrongholds(seed());
|
List<Position2> poss = getDimension().getStrongholds(seed());
|
||||||
@@ -130,7 +130,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
public IrisJigsawStructure guess(int x, int z) {
|
public IrisJigsawStructure guess(int x, int z) {
|
||||||
// todo The guess doesnt bring into account that the placer may return -1
|
// todo The guess doesnt bring into account that the placer may return -1
|
||||||
// todo doesnt bring skipped placements into account
|
// todo doesnt bring skipped placements into account
|
||||||
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
|
long seed = cng.fit(Integer.MIN_VALUE, Integer.MAX_VALUE, x, z);
|
||||||
IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8);
|
IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8);
|
||||||
IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8);
|
IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8);
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class IrisBiomeCustom {
|
|||||||
JSONObject po = new JSONObject();
|
JSONObject po = new JSONObject();
|
||||||
po.put("type", ambientParticle.getParticle().name().toLowerCase());
|
po.put("type", ambientParticle.getParticle().name().toLowerCase());
|
||||||
particle.put("options", po);
|
particle.put("options", po);
|
||||||
particle.put("probability", ambientParticle.getRarity());
|
particle.put("probability", 1f/ambientParticle.getRarity());
|
||||||
effects.put("particle", particle);
|
effects.put("particle", particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import com.volmit.iris.util.parallel.MultiBurst;
|
|||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.IrisLock;
|
import com.volmit.iris.util.scheduling.IrisLock;
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
import com.volmit.iris.util.scheduling.jobs.Job;
|
||||||
import com.volmit.iris.util.stream.ProceduralStream;
|
import com.volmit.iris.util.stream.ProceduralStream;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -63,7 +64,9 @@ import org.bukkit.util.Vector;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@@ -384,6 +387,88 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(OutputStream o, VolmitSender sender) throws IOException {
|
||||||
|
AtomicReference<IOException> ref = new AtomicReference<>();
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
new Job() {
|
||||||
|
private int total = getBlocks().size() * 3 + getStates().size();
|
||||||
|
private int c = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Saving Object";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
try {
|
||||||
|
DataOutputStream dos = new DataOutputStream(o);
|
||||||
|
dos.writeInt(w);
|
||||||
|
dos.writeInt(h);
|
||||||
|
dos.writeInt(d);
|
||||||
|
dos.writeUTF("Iris V2 IOB;");
|
||||||
|
|
||||||
|
KList<String> palette = new KList<>();
|
||||||
|
|
||||||
|
for (BlockData i : getBlocks().values()) {
|
||||||
|
palette.addIfMissing(i.getAsString());
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
total -= getBlocks().size() - palette.size();
|
||||||
|
|
||||||
|
dos.writeShort(palette.size());
|
||||||
|
|
||||||
|
for (String i : palette) {
|
||||||
|
dos.writeUTF(i);
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
|
||||||
|
dos.writeInt(getBlocks().size());
|
||||||
|
|
||||||
|
for (BlockVector i : getBlocks().keySet()) {
|
||||||
|
dos.writeShort(i.getBlockX());
|
||||||
|
dos.writeShort(i.getBlockY());
|
||||||
|
dos.writeShort(i.getBlockZ());
|
||||||
|
dos.writeShort(palette.indexOf(getBlocks().get(i).getAsString()));
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
|
||||||
|
dos.writeInt(getStates().size());
|
||||||
|
for (BlockVector i : getStates().keySet()) {
|
||||||
|
dos.writeShort(i.getBlockX());
|
||||||
|
dos.writeShort(i.getBlockY());
|
||||||
|
dos.writeShort(i.getBlockZ());
|
||||||
|
getStates().get(i).toBinary(dos);
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
ref.set(e);
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completeWork() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalWork() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWorkCompleted() {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}.execute(sender, true, () -> {});
|
||||||
|
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
if (ref.get() != null)
|
||||||
|
throw ref.get();
|
||||||
|
}
|
||||||
|
|
||||||
public void read(File file) throws IOException {
|
public void read(File file) throws IOException {
|
||||||
var fin = new BufferedInputStream(new FileInputStream(file));
|
var fin = new BufferedInputStream(new FileInputStream(file));
|
||||||
try {
|
try {
|
||||||
@@ -408,6 +493,16 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(File file, VolmitSender sender) throws IOException {
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
|
write(out, sender);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
public void shrinkwrap() {
|
public void shrinkwrap() {
|
||||||
BlockVector min = new BlockVector();
|
BlockVector min = new BlockVector();
|
||||||
BlockVector max = new BlockVector();
|
BlockVector max = new BlockVector();
|
||||||
@@ -478,7 +573,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
getBlocks().put(v, data);
|
getBlocks().put(v, data);
|
||||||
TileData state = TileData.getTileState(block);
|
TileData state = TileData.getTileState(block);
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
Iris.info("Saved State " + v);
|
Iris.debug("Saved State " + v);
|
||||||
getStates().put(v, state);
|
getStates().put(v, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.volmit.iris.Iris;
|
|||||||
import com.volmit.iris.engine.object.annotations.Desc;
|
import com.volmit.iris.engine.object.annotations.Desc;
|
||||||
import com.volmit.iris.engine.object.annotations.Snippet;
|
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -30,9 +31,12 @@ import org.bukkit.Axis;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.data.*;
|
import org.bukkit.block.data.*;
|
||||||
|
import org.bukkit.block.data.type.Wall;
|
||||||
|
import org.bukkit.block.structure.StructureRotation;
|
||||||
import org.bukkit.util.BlockVector;
|
import org.bukkit.util.BlockVector;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Snippet("object-rotator")
|
@Snippet("object-rotator")
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@@ -41,6 +45,8 @@ import java.util.List;
|
|||||||
@Desc("Configures rotation for iris")
|
@Desc("Configures rotation for iris")
|
||||||
@Data
|
@Data
|
||||||
public class IrisObjectRotation {
|
public class IrisObjectRotation {
|
||||||
|
private static final List<BlockFace> WALL_FACES = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST);
|
||||||
|
|
||||||
@Desc("If this rotator is enabled or not")
|
@Desc("If this rotator is enabled or not")
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
@@ -283,6 +289,22 @@ public class IrisObjectRotation {
|
|||||||
for (BlockFace i : faces) {
|
for (BlockFace i : faces) {
|
||||||
g.setFace(i, true);
|
g.setFace(i, true);
|
||||||
}
|
}
|
||||||
|
} else if (d instanceof Wall wall) {
|
||||||
|
KMap<BlockFace, Wall.Height> faces = new KMap<>();
|
||||||
|
|
||||||
|
for (BlockFace i : WALL_FACES) {
|
||||||
|
Wall.Height h = wall.getHeight(i);
|
||||||
|
BlockVector bv = new BlockVector(i.getModX(), i.getModY(), i.getModZ());
|
||||||
|
bv = rotate(bv.clone(), spinx, spiny, spinz);
|
||||||
|
BlockFace r = getFace(bv);
|
||||||
|
if (WALL_FACES.contains(r)) {
|
||||||
|
faces.put(r, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BlockFace i : WALL_FACES) {
|
||||||
|
wall.setHeight(i, faces.getOrDefault(i, Wall.Height.NONE));
|
||||||
|
}
|
||||||
} else if (d.getMaterial().equals(Material.NETHER_PORTAL) && d instanceof Orientable g) {
|
} else if (d.getMaterial().equals(Material.NETHER_PORTAL) && d instanceof Orientable g) {
|
||||||
//TODO: Fucks up logs
|
//TODO: Fucks up logs
|
||||||
BlockFace f = faceForAxis(g.getAxis());
|
BlockFace f = faceForAxis(g.getAxis());
|
||||||
|
|||||||
116
core/src/main/java/com/volmit/iris/server/EntryPoint.java
Normal file
116
core/src/main/java/com/volmit/iris/server/EntryPoint.java
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2024 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.volmit.iris.server;
|
||||||
|
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@Log(topic = "Iris-Server")
|
||||||
|
public class EntryPoint {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
if (args.length < 4) {
|
||||||
|
log.info("Usage: java -jar Iris.jar <version> <minMemory> <maxMemory> <server-port> [nodes]");
|
||||||
|
System.exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] nodes = new String[args.length - 4];
|
||||||
|
System.arraycopy(args, 4, nodes, 0, nodes.length);
|
||||||
|
try {
|
||||||
|
runServer(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]), Integer.parseInt(args[3]), nodes);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.log(Level.SEVERE, "Failed to start server", e);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runServer(String version, int minMemory, int maxMemory, int serverPort, String[] nodes) throws IOException {
|
||||||
|
File serverJar = new File("cache", "spigot-"+version+".jar");
|
||||||
|
if (!serverJar.getParentFile().exists() && !serverJar.getParentFile().mkdirs()) {
|
||||||
|
log.severe("Failed to create cache directory");
|
||||||
|
System.exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!serverJar.exists()) {
|
||||||
|
try (var in = new URL("https://download.getbukkit.org/spigot/spigot-"+ version+".jar").openStream()) {
|
||||||
|
Files.copy(in, serverJar.toPath());
|
||||||
|
}
|
||||||
|
log.info("Downloaded spigot-"+version+".jar to "+serverJar.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
File pluginFile = new File("plugins/Iris.jar");
|
||||||
|
if (pluginFile.exists()) pluginFile.delete();
|
||||||
|
if (!pluginFile.getParentFile().exists() && !pluginFile.getParentFile().mkdirs()) {
|
||||||
|
log.severe("Failed to create plugins directory");
|
||||||
|
System.exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean windows = System.getProperty("os.name").toLowerCase().contains("win");
|
||||||
|
String path = System.getProperty("java.home") + File.separator + "bin" + File.separator + (windows ? "java.exe" : "java");
|
||||||
|
|
||||||
|
try {
|
||||||
|
File irisFile = new File(EntryPoint.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||||
|
if (!irisFile.isFile()) {
|
||||||
|
log.severe("Failed to locate the Iris plugin jar");
|
||||||
|
System.exit(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Files.createSymbolicLink(pluginFile.toPath(), irisFile.toPath());
|
||||||
|
} catch (URISyntaxException ignored) {}
|
||||||
|
|
||||||
|
List<String> cmd = new ArrayList<>(List.of(
|
||||||
|
path,
|
||||||
|
"-Xms" + minMemory + "M",
|
||||||
|
"-Xmx" + maxMemory + "M",
|
||||||
|
"-XX:+AlwaysPreTouch",
|
||||||
|
"-XX:+HeapDumpOnOutOfMemoryError",
|
||||||
|
"-Ddisable.watchdog=true",
|
||||||
|
"-Dcom.mojang.eula.agree=true",
|
||||||
|
"-Dcom.volmit.iris.server.port="+serverPort
|
||||||
|
));
|
||||||
|
if (nodes.length > 0)
|
||||||
|
cmd.add("-Dcom.volmit.iris.server.remote=" + String.join(",", nodes));
|
||||||
|
cmd.addAll(List.of("-jar", serverJar.getAbsolutePath(), "nogui"));
|
||||||
|
|
||||||
|
var process = new ProcessBuilder(cmd)
|
||||||
|
.inheritIO()
|
||||||
|
.start();
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(process::destroy));
|
||||||
|
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
process.waitFor();
|
||||||
|
break;
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
199
core/src/main/java/com/volmit/iris/server/IrisConnection.java
Normal file
199
core/src/main/java/com/volmit/iris/server/IrisConnection.java
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package com.volmit.iris.server;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.execption.RejectedException;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.packet.handle.Decoder;
|
||||||
|
import com.volmit.iris.server.packet.handle.Encoder;
|
||||||
|
import com.volmit.iris.server.packet.handle.Prepender;
|
||||||
|
import com.volmit.iris.server.packet.handle.Splitter;
|
||||||
|
import com.volmit.iris.server.util.ErrorPacket;
|
||||||
|
import com.volmit.iris.server.util.ConnectionHolder;
|
||||||
|
import com.volmit.iris.server.util.PacketListener;
|
||||||
|
import com.volmit.iris.server.util.PacketSendListener;
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||||
|
import io.netty.handler.timeout.TimeoutException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Log(topic = "IrisConnection")
|
||||||
|
public class IrisConnection extends SimpleChannelInboundHandler<Packet> {
|
||||||
|
private static EventLoopGroup WORKER;
|
||||||
|
|
||||||
|
private Channel channel;
|
||||||
|
private SocketAddress address;
|
||||||
|
private final PacketListener listener;
|
||||||
|
private final Queue<PacketHolder> queue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception {
|
||||||
|
if (!channel.isOpen() || listener == null || !listener.isAccepting()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
listener.onPacket(packet);
|
||||||
|
} catch (RejectedException e) {
|
||||||
|
send(new ErrorPacket("Rejected: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Packet packet) {
|
||||||
|
this.send(packet, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Packet packet, @Nullable PacketSendListener listener) {
|
||||||
|
if (!isConnected()) {
|
||||||
|
queue.add(new PacketHolder(packet, listener));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flushQueue();
|
||||||
|
sendPacket(packet, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return channel != null && channel.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
try {
|
||||||
|
if (channel != null && channel.isOpen()) {
|
||||||
|
log.info("Closed on " + address);
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
if (listener != null)
|
||||||
|
listener.onDisconnect();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.log(Level.SEVERE, "Failed to close on " + address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(Runnable runnable) {
|
||||||
|
if (channel == null || !channel.isOpen()) return;
|
||||||
|
channel.eventLoop().execute(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushQueue() {
|
||||||
|
if (this.channel != null && this.channel.isOpen()) {
|
||||||
|
synchronized(this.queue) {
|
||||||
|
PacketHolder packetHolder;
|
||||||
|
while((packetHolder = this.queue.poll()) != null) {
|
||||||
|
sendPacket(packetHolder.packet, packetHolder.listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPacket(Packet packet, @Nullable PacketSendListener listener) {
|
||||||
|
if (!channel.eventLoop().inEventLoop()) {
|
||||||
|
channel.eventLoop().execute(() -> sendPacket(packet, listener));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelFuture channelFuture = channel.writeAndFlush(packet);
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
channelFuture.addListener(future -> {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
listener.onSuccess();
|
||||||
|
} else {
|
||||||
|
Packet fallback = listener.onFailure();
|
||||||
|
if (fallback == null) return;
|
||||||
|
channel.writeAndFlush(fallback)
|
||||||
|
.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
channelFuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
super.channelActive(ctx);
|
||||||
|
|
||||||
|
channel = ctx.channel();
|
||||||
|
address = channel.remoteAddress();
|
||||||
|
log.info("Opened on " + channel.remoteAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelInactive(ChannelHandlerContext ctx) {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
|
if (!channel.isOpen()) return;
|
||||||
|
ErrorPacket error;
|
||||||
|
if (cause instanceof TimeoutException) {
|
||||||
|
error = new ErrorPacket("Timed out");
|
||||||
|
} else {
|
||||||
|
error = new ErrorPacket("Internal Exception: " + cause.getMessage());
|
||||||
|
log.log(Level.SEVERE, "Failed to send packet", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPacket(error, PacketSendListener.thenRun(this::disconnect));
|
||||||
|
channel.config().setAutoRead(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "IrisConnection{address=%s}".formatted(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void configureSerialization(Channel channel, ConnectionHolder holder) {
|
||||||
|
channel.pipeline()
|
||||||
|
.addLast("timeout", new ReadTimeoutHandler(30))
|
||||||
|
.addLast("splitter", new Splitter())
|
||||||
|
.addLast("decoder", new Decoder())
|
||||||
|
.addLast("prepender", new Prepender())
|
||||||
|
.addLast("encoder", new Encoder())
|
||||||
|
.addLast("packet_handler", holder.getConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ConnectionHolder> T connect(InetSocketAddress address, T holder) throws InterruptedException {
|
||||||
|
new Bootstrap()
|
||||||
|
.group(getWorker())
|
||||||
|
.handler(new ChannelInitializer<>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel) {
|
||||||
|
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
|
||||||
|
IrisConnection.configureSerialization(channel, holder);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.channel(NioSocketChannel.class)
|
||||||
|
.connect(address)
|
||||||
|
.sync();
|
||||||
|
return holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PacketHolder {
|
||||||
|
private final Packet packet;
|
||||||
|
@Nullable
|
||||||
|
private final PacketSendListener listener;
|
||||||
|
|
||||||
|
public PacketHolder(Packet packet, @Nullable PacketSendListener listener) {
|
||||||
|
this.packet = packet;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EventLoopGroup getWorker() {
|
||||||
|
if (WORKER == null) {
|
||||||
|
WORKER = new NioEventLoopGroup();
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> WORKER.shutdownGracefully()));
|
||||||
|
}
|
||||||
|
return WORKER;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.volmit.iris.server.execption;
|
||||||
|
|
||||||
|
public class RejectedException extends Exception {
|
||||||
|
|
||||||
|
public RejectedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.volmit.iris.server.master;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import com.volmit.iris.server.packet.init.InfoPacket;
|
||||||
|
import com.volmit.iris.server.packet.init.PingPacket;
|
||||||
|
import com.volmit.iris.server.util.ConnectionHolder;
|
||||||
|
import com.volmit.iris.server.util.PacketListener;
|
||||||
|
import com.volmit.iris.server.util.PacketSendListener;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class IrisMasterClient implements ConnectionHolder, PacketListener {
|
||||||
|
private final @Getter IrisConnection connection = new IrisConnection(this);
|
||||||
|
private final IrisMasterSession session;
|
||||||
|
private final CompletableFuture<PingPacket> pingResponse = new CompletableFuture<>();
|
||||||
|
private final CompletableFuture<Integer> nodeCount = new CompletableFuture<>();
|
||||||
|
|
||||||
|
IrisMasterClient(String version, IrisMasterSession session){
|
||||||
|
this.session = session;
|
||||||
|
Packets.PING.newPacket()
|
||||||
|
.setVersion(version)
|
||||||
|
.send(connection);
|
||||||
|
try {
|
||||||
|
var packet = pingResponse.get();
|
||||||
|
if (!packet.getVersion().contains(version))
|
||||||
|
throw new IllegalStateException("Remote server version does not match");
|
||||||
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
throw new IllegalStateException("Failed to get ping packet", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void send(Packet packet) {
|
||||||
|
connection.send(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void send(Packet packet, @Nullable PacketSendListener listener) {
|
||||||
|
connection.send(packet, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacket(Packet raw) {
|
||||||
|
if (!pingResponse.isDone() && raw instanceof PingPacket packet) {
|
||||||
|
pingResponse.complete(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!nodeCount.isDone() && raw instanceof InfoPacket packet && packet.getNodeCount() > 0) {
|
||||||
|
nodeCount.complete(packet.getNodeCount());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
session.onClientPacket(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNodeCount() {
|
||||||
|
try {
|
||||||
|
return nodeCount.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package com.volmit.iris.server.master;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.node.IrisServer;
|
||||||
|
import com.volmit.iris.server.util.PregenHolder;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@Log(topic = "Iris-MasterServer")
|
||||||
|
public class IrisMasterServer extends IrisServer {
|
||||||
|
private static IrisMasterServer instance;
|
||||||
|
private final KMap<UUID, KMap<IrisMasterClient, KMap<UUID, PregenHolder>>> sessions = new KMap<>();
|
||||||
|
private final KMap<String, KSet<InetSocketAddress>> nodes = new KMap<>();
|
||||||
|
|
||||||
|
public IrisMasterServer(int port, String[] remote) throws InterruptedException {
|
||||||
|
super("Iris-MasterServer", port, IrisMasterSession::new);
|
||||||
|
if (instance != null && !instance.isRunning())
|
||||||
|
close("Server already running");
|
||||||
|
instance = this;
|
||||||
|
|
||||||
|
for (var address : remote) {
|
||||||
|
try {
|
||||||
|
var split = address.split(":");
|
||||||
|
if (split.length != 2 || !split[1].matches("\\d+")) {
|
||||||
|
log.warning("Invalid remote server address: " + address);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
addNode(new InetSocketAddress(split[0], Integer.parseInt(split[1])));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.log(Level.WARNING, "Failed to parse address: " + address, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addNode(InetSocketAddress address) throws InterruptedException {
|
||||||
|
var ping = new PingConnection(address);
|
||||||
|
try {
|
||||||
|
for (String version : ping.getVersion().get())
|
||||||
|
addNode(address, version);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNode(InetSocketAddress address, String version) {
|
||||||
|
nodes.computeIfAbsent(version, v -> new KSet<>()).add(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeNode(InetSocketAddress address) {
|
||||||
|
for (var set : nodes.values()) {
|
||||||
|
set.remove(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void close(UUID session) {
|
||||||
|
var map = get().sessions.remove(session);
|
||||||
|
if (map == null) return;
|
||||||
|
map.keySet().forEach(IrisMasterClient::disconnect);
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KList<String> getVersions() {
|
||||||
|
return get().nodes.k();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KMap<IrisMasterClient, KMap<UUID, PregenHolder>> getNodes(String version, IrisMasterSession session) {
|
||||||
|
var master = get();
|
||||||
|
var uuid = session.getUuid();
|
||||||
|
close(uuid);
|
||||||
|
|
||||||
|
master.getLogger().info("Requesting nodes for session " + uuid);
|
||||||
|
var map = new KMap<IrisMasterClient, KMap<UUID, PregenHolder>>();
|
||||||
|
for (var address : master.nodes.getOrDefault(version, new KSet<>())) {
|
||||||
|
try {
|
||||||
|
map.put(IrisConnection.connect(address, new IrisMasterClient(version, session)), new KMap<>());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
master.getLogger().log(Level.WARNING, "Failed to connect to server " + address, e);
|
||||||
|
master.removeNode(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
master.sessions.put(uuid, map);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
log.info("Closing!");
|
||||||
|
super.close();
|
||||||
|
sessions.values()
|
||||||
|
.stream()
|
||||||
|
.map(KMap::keySet)
|
||||||
|
.flatMap(Set::stream)
|
||||||
|
.forEach(IrisMasterClient::disconnect);
|
||||||
|
sessions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Logger getLogger() {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close(String message) throws IllegalStateException {
|
||||||
|
try {
|
||||||
|
close();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
throw new IllegalStateException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IrisMasterServer get() {
|
||||||
|
if (instance == null)
|
||||||
|
throw new IllegalStateException("IrisMasterServer not running");
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package com.volmit.iris.server.master;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.execption.RejectedException;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import com.volmit.iris.server.packet.init.EnginePacket;
|
||||||
|
import com.volmit.iris.server.packet.init.FilePacket;
|
||||||
|
import com.volmit.iris.server.packet.init.InfoPacket;
|
||||||
|
import com.volmit.iris.server.packet.init.PingPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.ChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.DonePacket;
|
||||||
|
import com.volmit.iris.server.packet.work.MantleChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.PregenPacket;
|
||||||
|
import com.volmit.iris.server.util.*;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@Log(topic = "IrisMasterSession")
|
||||||
|
public class IrisMasterSession implements ConnectionHolder, PacketListener {
|
||||||
|
private final @Getter IrisConnection connection = new IrisConnection(this);
|
||||||
|
private final @Getter UUID uuid = UUID.randomUUID();
|
||||||
|
private final KMap<UUID, IrisMasterClient> map = new KMap<>();
|
||||||
|
private final CPSLooper cpsLooper = new CPSLooper("IrisMasterSession-" + uuid, connection);
|
||||||
|
private final KMap<UUID, CompletingHolder> waiting = new KMap<>();
|
||||||
|
private KMap<IrisMasterClient, KMap<UUID, PregenHolder>> clients;
|
||||||
|
private int radius = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacket(Packet raw) throws Exception {
|
||||||
|
if (clients == null) {
|
||||||
|
if (raw instanceof PingPacket packet) {
|
||||||
|
var versions = packet.getVersion();
|
||||||
|
PacketSendListener listener = versions.size() != 1 ? PacketSendListener.thenRun(connection::disconnect) : null;
|
||||||
|
if (listener == null) {
|
||||||
|
clients = IrisMasterServer.getNodes(versions.get(0), this);
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
connection.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeCount = clients.keySet()
|
||||||
|
.stream()
|
||||||
|
.mapToInt(IrisMasterClient::getNodeCount)
|
||||||
|
.sum();
|
||||||
|
cpsLooper.setNodeCount(nodeCount);
|
||||||
|
}
|
||||||
|
packet.setVersion(IrisMasterServer.getVersions())
|
||||||
|
.send(connection, listener);
|
||||||
|
} else throw new RejectedException("Not a ping packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw instanceof FilePacket packet) {
|
||||||
|
if (radius != -1)
|
||||||
|
throw new RejectedException("Engine already setup");
|
||||||
|
waiting.put(packet.getId(), new CompletingHolder(clients.k()));
|
||||||
|
clients.keySet().forEach(client -> client.send(packet));
|
||||||
|
} else if (raw instanceof EnginePacket packet) {
|
||||||
|
if (radius != -1)
|
||||||
|
throw new RejectedException("Engine already setup");
|
||||||
|
radius = packet.getRadius();
|
||||||
|
waiting.put(packet.getId(), new CompletingHolder(clients.k()));
|
||||||
|
clients.keySet().forEach(client -> client.send(packet));
|
||||||
|
} else if (raw instanceof PregenPacket packet) {
|
||||||
|
if (radius == -1)
|
||||||
|
throw new RejectedException("Engine not setup");
|
||||||
|
var client = pick();
|
||||||
|
map.put(packet.getId(), client);
|
||||||
|
new PregenHolder(packet, radius, true, null)
|
||||||
|
.put(clients.get(client));
|
||||||
|
|
||||||
|
client.send(packet);
|
||||||
|
} else if (raw instanceof MantleChunkPacket packet) {
|
||||||
|
var client = map.get(packet.getPregenId());
|
||||||
|
client.send(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onClientPacket(IrisMasterClient client, Packet raw) {
|
||||||
|
if (raw instanceof ErrorPacket packet) {
|
||||||
|
packet.log(log, Level.SEVERE);
|
||||||
|
} else if (raw instanceof ChunkPacket) {
|
||||||
|
connection.send(raw);
|
||||||
|
} else if (raw instanceof MantleChunkPacket packet) {
|
||||||
|
var map = clients.get(client);
|
||||||
|
if (map.get(packet.getPregenId())
|
||||||
|
.remove(packet.getX(), packet.getZ()))
|
||||||
|
map.get(packet.getPregenId());
|
||||||
|
|
||||||
|
connection.send(packet);
|
||||||
|
} else if (raw instanceof MantleChunkPacket.Request packet) {
|
||||||
|
connection.send(packet);
|
||||||
|
} else if (raw instanceof InfoPacket packet) {
|
||||||
|
int i = packet.getGenerated();
|
||||||
|
if (i != -1) cpsLooper.addChunks(i);
|
||||||
|
} else if (raw instanceof DonePacket packet) {
|
||||||
|
var holder = waiting.get(packet.getId());
|
||||||
|
if (holder.remove(client)) {
|
||||||
|
connection.send(Packets.DONE.newPacket().setId(packet.getId()));
|
||||||
|
waiting.remove(packet.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect() {
|
||||||
|
IrisMasterServer.close(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrisMasterClient pick() throws RejectedException {
|
||||||
|
return clients.keySet()
|
||||||
|
.stream()
|
||||||
|
.min(Comparator.comparingInt(c -> clients.get(c).size()))
|
||||||
|
.orElseThrow(() -> new RejectedException("No clients available"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record CompletingHolder(KList<IrisMasterClient> clients) {
|
||||||
|
|
||||||
|
public synchronized boolean remove(IrisMasterClient client) {
|
||||||
|
clients.remove(client);
|
||||||
|
return clients.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.volmit.iris.server.master;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import com.volmit.iris.server.packet.init.PingPacket;
|
||||||
|
import com.volmit.iris.server.util.ConnectionHolder;
|
||||||
|
import com.volmit.iris.server.util.PacketListener;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class PingConnection implements ConnectionHolder, PacketListener {
|
||||||
|
private final IrisConnection connection = new IrisConnection(this);
|
||||||
|
private final CompletableFuture<KList<String>> version = new CompletableFuture<>();
|
||||||
|
|
||||||
|
public PingConnection(InetSocketAddress address) throws InterruptedException {
|
||||||
|
IrisConnection.connect(address, this);
|
||||||
|
Packets.PING.newPacket().send(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacket(Packet packet) throws Exception {
|
||||||
|
if (packet instanceof PingPacket p) {
|
||||||
|
version.complete(p.getVersion());
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.volmit.iris.server.node;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.util.ConnectionHolder;
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.logging.LogLevel;
|
||||||
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@Log(topic = "Iris-Server")
|
||||||
|
public class IrisServer implements AutoCloseable {
|
||||||
|
private final NioEventLoopGroup bossGroup, workerGroup;
|
||||||
|
private final Channel channel;
|
||||||
|
private @Getter boolean running = true;
|
||||||
|
|
||||||
|
public IrisServer(int port) throws InterruptedException {
|
||||||
|
this("Iris-Server", port, IrisSession::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IrisServer(String name, int port, Supplier<ConnectionHolder> factory) throws InterruptedException {
|
||||||
|
bossGroup = new NioEventLoopGroup(1);
|
||||||
|
workerGroup = new NioEventLoopGroup();
|
||||||
|
|
||||||
|
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||||
|
bootstrap.group(bossGroup, workerGroup)
|
||||||
|
.channel(NioServerSocketChannel.class)
|
||||||
|
.handler(new LoggingHandler(name, LogLevel.DEBUG))
|
||||||
|
.childHandler(new Initializer(factory));
|
||||||
|
|
||||||
|
channel = bootstrap.bind(port).sync().channel();
|
||||||
|
|
||||||
|
getLogger().info("Started on port " + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
if (!running) return;
|
||||||
|
running = false;
|
||||||
|
channel.close().sync();
|
||||||
|
bossGroup.shutdownGracefully();
|
||||||
|
workerGroup.shutdownGracefully();
|
||||||
|
|
||||||
|
getLogger().info("Stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Logger getLogger() {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class Initializer extends ChannelInitializer<Channel> {
|
||||||
|
private final Supplier<ConnectionHolder> factory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel ch) {
|
||||||
|
IrisConnection.configureSerialization(ch, factory.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
core/src/main/java/com/volmit/iris/server/node/IrisSession.java
Normal file
113
core/src/main/java/com/volmit/iris/server/node/IrisSession.java
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package com.volmit.iris.server.node;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.nms.IHeadless;
|
||||||
|
import com.volmit.iris.core.nms.INMS;
|
||||||
|
import com.volmit.iris.engine.data.cache.Cache;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.execption.RejectedException;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import com.volmit.iris.server.packet.init.EnginePacket;
|
||||||
|
import com.volmit.iris.server.packet.init.FilePacket;
|
||||||
|
import com.volmit.iris.server.packet.init.PingPacket;
|
||||||
|
import com.volmit.iris.server.util.CPSLooper;
|
||||||
|
import com.volmit.iris.server.util.ConnectionHolder;
|
||||||
|
import com.volmit.iris.server.util.PacketListener;
|
||||||
|
import com.volmit.iris.server.packet.work.ChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.MantleChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.PregenPacket;
|
||||||
|
import com.volmit.iris.server.util.PregenHolder;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.io.IO;
|
||||||
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class IrisSession implements ConnectionHolder, PacketListener {
|
||||||
|
private static final MultiBurst burst = new MultiBurst("IrisHeadless", 8);
|
||||||
|
private final @Getter IrisConnection connection = new IrisConnection(this);
|
||||||
|
private final File base = new File("cache/" + UUID.randomUUID());
|
||||||
|
private final KMap<UUID, PregenHolder> chunks = new KMap<>();
|
||||||
|
private final KMap<Long, PregenHolder> pregens = new KMap<>();
|
||||||
|
private final CPSLooper cpsLooper = new CPSLooper("IrisSession-"+base.getName(), connection);
|
||||||
|
|
||||||
|
private Engine engine;
|
||||||
|
private IHeadless headless;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacket(Packet raw) throws Exception {
|
||||||
|
cpsLooper.setNodeCount(1);
|
||||||
|
if (raw instanceof FilePacket packet) {
|
||||||
|
if (engine != null) throw new RejectedException("Engine already setup");
|
||||||
|
|
||||||
|
packet.write(base);
|
||||||
|
Packets.DONE.newPacket()
|
||||||
|
.setId(packet.getId())
|
||||||
|
.send(connection);
|
||||||
|
} else if (raw instanceof EnginePacket packet) {
|
||||||
|
if (engine != null) throw new RejectedException("Engine already setup");
|
||||||
|
engine = packet.getEngine(base);
|
||||||
|
headless = INMS.get().createHeadless(engine);
|
||||||
|
headless.setSession(this);
|
||||||
|
|
||||||
|
Packets.DONE.newPacket()
|
||||||
|
.setId(packet.getId())
|
||||||
|
.send(connection);
|
||||||
|
} else if (raw instanceof MantleChunkPacket packet) {
|
||||||
|
if (engine == null) throw new RejectedException("Engine not setup");
|
||||||
|
packet.set(engine.getMantle().getMantle());
|
||||||
|
|
||||||
|
var holder = chunks.get(packet.getPregenId());
|
||||||
|
if (holder.remove(packet.getX(), packet.getZ())) {
|
||||||
|
headless.generateRegion(burst, holder.getX(), holder.getZ(), 20, null)
|
||||||
|
.thenRun(() -> {
|
||||||
|
holder.iterate(chunkPos -> {
|
||||||
|
var resp = Packets.MANTLE_CHUNK.newPacket();
|
||||||
|
resp.setPregenId(holder.getId());
|
||||||
|
resp.read(chunkPos, engine.getMantle().getMantle());
|
||||||
|
connection.send(resp);
|
||||||
|
});
|
||||||
|
chunks.remove(holder.getId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (raw instanceof PregenPacket packet) {
|
||||||
|
if (engine == null) throw new RejectedException("Engine not setup");
|
||||||
|
var radius = engine.getMantle().getRadius();
|
||||||
|
|
||||||
|
var holder = new PregenHolder(packet, radius, true, null);
|
||||||
|
var request = Packets.MANTLE_CHUNK_REQUEST.newPacket()
|
||||||
|
.setPregenId(packet.getId());
|
||||||
|
holder.iterate(request::add);
|
||||||
|
var pregen = new PregenHolder(packet, 0, true, null);
|
||||||
|
pregens.put(Cache.key(pregen.getX(), pregen.getZ()), pregen);
|
||||||
|
|
||||||
|
chunks.put(packet.getId(), holder);
|
||||||
|
connection.send(request);
|
||||||
|
} else if (raw instanceof PingPacket packet) {
|
||||||
|
packet.setBukkit().send(connection);
|
||||||
|
} else throw new RejectedException("Unhandled packet: " + raw.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void completeChunk(int x, int z, byte[] data) {
|
||||||
|
cpsLooper.addChunks(1);
|
||||||
|
long id = Cache.key(x >> 5, z >> 5);
|
||||||
|
var pregen = pregens.get(id);
|
||||||
|
if (pregen.remove(x, z))
|
||||||
|
pregens.remove(id);
|
||||||
|
connection.send(new ChunkPacket(pregen.getId(), x, z, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect() {
|
||||||
|
if (engine != null) {
|
||||||
|
engine.close();
|
||||||
|
engine = null;
|
||||||
|
headless = null;
|
||||||
|
}
|
||||||
|
cpsLooper.exit();
|
||||||
|
IO.delete(base);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
core/src/main/java/com/volmit/iris/server/packet/Packet.java
Normal file
24
core/src/main/java/com/volmit/iris/server/packet/Packet.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package com.volmit.iris.server.packet;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.util.PacketSendListener;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface Packet {
|
||||||
|
void read(ByteBuf byteBuf) throws IOException;
|
||||||
|
void write(ByteBuf byteBuf) throws IOException;
|
||||||
|
|
||||||
|
default Packets<?> getType() {
|
||||||
|
return Packets.get(getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
default void send(IrisConnection connection) {
|
||||||
|
send(connection, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void send(IrisConnection connection, PacketSendListener listener) {
|
||||||
|
connection.send(this, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.volmit.iris.server.packet;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.init.EnginePacket;
|
||||||
|
import com.volmit.iris.server.packet.init.FilePacket;
|
||||||
|
import com.volmit.iris.server.packet.init.InfoPacket;
|
||||||
|
import com.volmit.iris.server.packet.init.PingPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.DonePacket;
|
||||||
|
import com.volmit.iris.server.util.ErrorPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.ChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.MantleChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.PregenPacket;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class Packets<T extends Packet> {
|
||||||
|
private static final List<Packets<? extends Packet>> REGISTRY;
|
||||||
|
private static final Map<Class<? extends Packet>, Packets<? extends Packet>> MAP;
|
||||||
|
|
||||||
|
public static final Packets<ErrorPacket> ERROR;
|
||||||
|
public static final Packets<InfoPacket> INFO;
|
||||||
|
public static final Packets<PingPacket> PING;
|
||||||
|
public static final Packets<FilePacket> FILE;
|
||||||
|
public static final Packets<EnginePacket> ENGINE;
|
||||||
|
|
||||||
|
public static final Packets<DonePacket> DONE;
|
||||||
|
public static final Packets<PregenPacket> PREGEN;
|
||||||
|
public static final Packets<ChunkPacket> CHUNK;
|
||||||
|
public static final Packets<MantleChunkPacket> MANTLE_CHUNK;
|
||||||
|
public static final Packets<MantleChunkPacket.Request> MANTLE_CHUNK_REQUEST;
|
||||||
|
|
||||||
|
private final Class<T> type;
|
||||||
|
private final Supplier<T> factory;
|
||||||
|
private int id = -1;
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
if (id == -1) throw new IllegalStateException("Unknown packet type: " + this);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T newPacket() {
|
||||||
|
return factory.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Packets<? extends Packet> get(int id) {
|
||||||
|
return REGISTRY.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Packet newPacket(int id) {
|
||||||
|
return get(id).newPacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static <T extends Packet> Packets<T> get(Class<T> type) {
|
||||||
|
var t = MAP.get(type);
|
||||||
|
if (t == null) throw new IllegalArgumentException("Unknown packet type: " + type);
|
||||||
|
return (Packets<T>) t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getId(Class<? extends Packet> type) {
|
||||||
|
return get(type).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
ERROR = new Packets<>(ErrorPacket.class, ErrorPacket::new);
|
||||||
|
INFO = new Packets<>(InfoPacket.class, InfoPacket::new);
|
||||||
|
PING = new Packets<>(PingPacket.class, PingPacket::new);
|
||||||
|
FILE = new Packets<>(FilePacket.class, FilePacket::new);
|
||||||
|
ENGINE = new Packets<>(EnginePacket.class, EnginePacket::new);
|
||||||
|
|
||||||
|
DONE = new Packets<>(DonePacket.class, DonePacket::new);
|
||||||
|
PREGEN = new Packets<>(PregenPacket.class, PregenPacket::new);
|
||||||
|
CHUNK = new Packets<>(ChunkPacket.class, ChunkPacket::new);
|
||||||
|
MANTLE_CHUNK = new Packets<>(MantleChunkPacket.class, MantleChunkPacket::new);
|
||||||
|
MANTLE_CHUNK_REQUEST = new Packets<>(MantleChunkPacket.Request.class, MantleChunkPacket.Request::new);
|
||||||
|
|
||||||
|
REGISTRY = List.of(ERROR, INFO, PING, FILE, ENGINE, DONE, PREGEN, CHUNK, MANTLE_CHUNK, MANTLE_CHUNK_REQUEST);
|
||||||
|
|
||||||
|
var map = new HashMap<Class<? extends Packet>, Packets<? extends Packet>>();
|
||||||
|
for (int i = 0; i < REGISTRY.size(); i++) {
|
||||||
|
var entry = REGISTRY.get(i);
|
||||||
|
entry.id = i;
|
||||||
|
map.put(entry.type, entry);
|
||||||
|
}
|
||||||
|
MAP = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.volmit.iris.server.packet.handle;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Decoder extends ByteToMessageDecoder {
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||||
|
var packet = Packets.newPacket(byteBuf.readByte());
|
||||||
|
packet.read(byteBuf);
|
||||||
|
list.add(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.volmit.iris.server.packet.handle;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
|
||||||
|
public class Encoder extends MessageToByteEncoder<Packet> {
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf byteBuf) throws Exception {
|
||||||
|
byteBuf.writeByte(packet.getType().getId());
|
||||||
|
packet.write(byteBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.volmit.iris.server.packet.handle;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
|
||||||
|
public class Prepender extends MessageToByteEncoder<ByteBuf> {
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
|
||||||
|
int i = in.readableBytes();
|
||||||
|
out.writeInt(i);
|
||||||
|
out.writeBytes(in, in.readerIndex(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.volmit.iris.server.packet.handle;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Splitter extends ByteToMessageDecoder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception {
|
||||||
|
if (!byteBuf.isReadable(4))
|
||||||
|
return;
|
||||||
|
byteBuf.markReaderIndex();
|
||||||
|
|
||||||
|
byte[] bytes = new byte[4];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
var buffer = Unpooled.wrappedBuffer(bytes);
|
||||||
|
try {
|
||||||
|
int j = buffer.readInt();
|
||||||
|
if (byteBuf.readableBytes() >= j) {
|
||||||
|
list.add(byteBuf.readBytes(j));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byteBuf.resetReaderIndex();
|
||||||
|
} finally {
|
||||||
|
buffer.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.volmit.iris.server.packet.init;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.framework.EngineTarget;
|
||||||
|
import com.volmit.iris.engine.object.IrisWorld;
|
||||||
|
import com.volmit.iris.server.util.ByteBufUtil;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class EnginePacket implements Packet {
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
private String dimension;
|
||||||
|
private long seed;
|
||||||
|
private int radius;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
id = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
dimension = ByteBufUtil.readString(byteBuf);
|
||||||
|
seed = byteBuf.readLong();
|
||||||
|
radius = byteBuf.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(id.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(id.getLeastSignificantBits());
|
||||||
|
ByteBufUtil.writeString(byteBuf, dimension);
|
||||||
|
byteBuf.writeLong(seed);
|
||||||
|
byteBuf.writeInt(radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Engine getEngine(File base) {
|
||||||
|
var data = IrisData.get(new File(base, "iris/pack"));
|
||||||
|
var type = data.getDimensionLoader().load(dimension);
|
||||||
|
var world = IrisWorld.builder()
|
||||||
|
.name(base.getName())
|
||||||
|
.seed(seed)
|
||||||
|
.worldFolder(base)
|
||||||
|
.minHeight(type.getMinHeight())
|
||||||
|
.maxHeight(type.getMaxHeight())
|
||||||
|
.environment(type.getEnvironment())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new IrisEngine(new EngineTarget(world, type, data), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.volmit.iris.server.packet.init;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.util.ByteBufUtil;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class FilePacket implements Packet {
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
private String path;
|
||||||
|
private long offset;
|
||||||
|
private long length;
|
||||||
|
private byte[] data;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
id = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
path = ByteBufUtil.readString(byteBuf);
|
||||||
|
offset = byteBuf.readLong();
|
||||||
|
length = byteBuf.readLong();
|
||||||
|
data = ByteBufUtil.readBytes(byteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(id.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(id.getLeastSignificantBits());
|
||||||
|
ByteBufUtil.writeString(byteBuf, path);
|
||||||
|
byteBuf.writeLong(offset);
|
||||||
|
byteBuf.writeLong(length);
|
||||||
|
ByteBufUtil.writeBytes(byteBuf, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(File base) throws IOException {
|
||||||
|
File f = new File(base, path);
|
||||||
|
if (!f.getAbsolutePath().startsWith(base.getAbsolutePath()))
|
||||||
|
throw new IOException("Invalid path " + path);
|
||||||
|
if (!f.getParentFile().exists() && !f.getParentFile().mkdirs())
|
||||||
|
throw new IOException("Failed to create directory " + f.getParentFile());
|
||||||
|
|
||||||
|
try (var raf = new RandomAccessFile(f, "rws")) {
|
||||||
|
if (raf.length() < length)
|
||||||
|
raf.setLength(length);
|
||||||
|
raf.seek(offset);
|
||||||
|
raf.write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.volmit.iris.server.packet.init;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InfoPacket implements Packet {
|
||||||
|
private int nodeCount = -1;
|
||||||
|
private int cps = -1;
|
||||||
|
private int generated = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
nodeCount = byteBuf.readInt();
|
||||||
|
cps = byteBuf.readInt();
|
||||||
|
generated = byteBuf.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeInt(nodeCount);
|
||||||
|
byteBuf.writeInt(cps);
|
||||||
|
byteBuf.writeInt(generated);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.volmit.iris.server.packet.init;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.util.ByteBufUtil;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class PingPacket implements Packet {
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
private KList<String> version = new KList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
id = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
int size = byteBuf.readInt();
|
||||||
|
|
||||||
|
version = new KList<>();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
version.add(ByteBufUtil.readString(byteBuf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(id.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(id.getLeastSignificantBits());
|
||||||
|
|
||||||
|
byteBuf.writeInt(version.size());
|
||||||
|
for (String s : version) {
|
||||||
|
ByteBufUtil.writeString(byteBuf, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PingPacket setBukkit() {
|
||||||
|
this.version = new KList<>(Bukkit.getBukkitVersion().split("-")[0]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PingPacket setVersion(String version) {
|
||||||
|
this.version = new KList<>(version);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PingPacket setVersion(KList<String> version) {
|
||||||
|
this.version = version;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.volmit.iris.server.packet.work;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.util.ByteBufUtil;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ChunkPacket implements Packet {
|
||||||
|
private UUID pregenId;
|
||||||
|
private int x, z;
|
||||||
|
private byte[] data;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
pregenId = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
x = byteBuf.readInt();
|
||||||
|
z = byteBuf.readInt();
|
||||||
|
data = ByteBufUtil.readBytes(byteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(pregenId.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(pregenId.getLeastSignificantBits());
|
||||||
|
byteBuf.writeInt(x);
|
||||||
|
byteBuf.writeInt(z);
|
||||||
|
ByteBufUtil.writeBytes(byteBuf, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.volmit.iris.server.packet.work;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class DonePacket implements Packet {
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
id = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(id.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(id.getLeastSignificantBits());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.volmit.iris.server.packet.work;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.util.ByteBufUtil;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.mantle.MantleChunk;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||||
|
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class MantleChunkPacket implements Packet {
|
||||||
|
private UUID pregenId;
|
||||||
|
private int x, z;
|
||||||
|
private MantleChunk chunk;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
pregenId = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
x = byteBuf.readInt();
|
||||||
|
z = byteBuf.readInt();
|
||||||
|
int sectionHeight = byteBuf.readInt();
|
||||||
|
try (var din = new DataInputStream(new BufferedInputStream(new LZ4BlockInputStream(new ByteArrayInputStream(ByteBufUtil.readBytes(byteBuf)))))) {
|
||||||
|
chunk = new MantleChunk(sectionHeight, din);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new IOException("Failed to read chunk", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(pregenId.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(pregenId.getLeastSignificantBits());
|
||||||
|
byteBuf.writeInt(x);
|
||||||
|
byteBuf.writeInt(z);
|
||||||
|
byteBuf.writeInt(chunk.getSectionHeight());
|
||||||
|
var out = new ByteArrayOutputStream();
|
||||||
|
try (var dos = new DataOutputStream(new LZ4BlockOutputStream(out))) {
|
||||||
|
chunk.write(dos);
|
||||||
|
}
|
||||||
|
ByteBufUtil.writeBytes(byteBuf, out.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public MantleChunkPacket read(Position2 pos, Mantle mantle) {
|
||||||
|
this.x = pos.getX();
|
||||||
|
this.z = pos.getZ();
|
||||||
|
this.chunk = mantle.getChunk(x, z);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(Mantle mantle) {
|
||||||
|
mantle.setChunk(x, z, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class Request implements Packet {
|
||||||
|
private UUID pregenId;
|
||||||
|
private KList<Position2> positions = new KList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
pregenId = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
var count = byteBuf.readInt();
|
||||||
|
positions = new KList<>(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
positions.add(new Position2(byteBuf.readInt(), byteBuf.readInt()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(pregenId.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(pregenId.getLeastSignificantBits());
|
||||||
|
byteBuf.writeInt(positions.size());
|
||||||
|
for (Position2 p : positions) {
|
||||||
|
byteBuf.writeInt(p.getX());
|
||||||
|
byteBuf.writeInt(p.getZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public void add(Position2 chunkPos) {
|
||||||
|
if (positions == null)
|
||||||
|
positions = new KList<>();
|
||||||
|
positions.add(chunkPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.volmit.iris.server.packet.work;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PregenPacket implements Packet {
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
private int x, z;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
id = new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||||
|
x = byteBuf.readInt();
|
||||||
|
z = byteBuf.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
byteBuf.writeLong(id.getMostSignificantBits());
|
||||||
|
byteBuf.writeLong(id.getLeastSignificantBits());
|
||||||
|
byteBuf.writeInt(x);
|
||||||
|
byteBuf.writeInt(z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
package com.volmit.iris.server.pregen;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.core.gui.PregeneratorJob;
|
||||||
|
import com.volmit.iris.core.nms.IHeadless;
|
||||||
|
import com.volmit.iris.core.nms.INMS;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.execption.RejectedException;
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import com.volmit.iris.server.packet.init.InfoPacket;
|
||||||
|
import com.volmit.iris.server.packet.init.PingPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.ChunkPacket;
|
||||||
|
import com.volmit.iris.server.packet.work.DonePacket;
|
||||||
|
import com.volmit.iris.server.packet.work.MantleChunkPacket;
|
||||||
|
import com.volmit.iris.server.util.*;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@Log(topic = "CloudPregen")
|
||||||
|
public class CloudMethod implements PregeneratorMethod, ConnectionHolder, PacketListener {
|
||||||
|
private final @Getter IrisConnection connection = new IrisConnection(this);
|
||||||
|
private final Engine engine;
|
||||||
|
private final IHeadless headless;
|
||||||
|
private final KMap<UUID, PregenHolder> holders = new KMap<>();
|
||||||
|
private final CompletableFuture<LimitedSemaphore> future = new CompletableFuture<>();
|
||||||
|
private final KMap<UUID, CompletableFuture<Object>> locks = new KMap<>();
|
||||||
|
|
||||||
|
public CloudMethod(String address, Engine engine) throws InterruptedException {
|
||||||
|
var split = address.split(":");
|
||||||
|
if (split.length != 2 || !split[1].matches("\\d+"))
|
||||||
|
throw new IllegalArgumentException("Invalid remote server address: " + address);
|
||||||
|
|
||||||
|
IrisConnection.connect(new InetSocketAddress(split[0], Integer.parseInt(split[1])), this);
|
||||||
|
|
||||||
|
this.engine = engine;
|
||||||
|
this.headless = INMS.get().createHeadless(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
var studio = engine.isStudio();
|
||||||
|
var base = studio ?
|
||||||
|
engine.getData().getDataFolder() :
|
||||||
|
engine.getWorld().worldFolder();
|
||||||
|
var name = engine.getWorld().name();
|
||||||
|
var exit = new AtomicBoolean(false);
|
||||||
|
var limited = new LimitedSemaphore(IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()));
|
||||||
|
|
||||||
|
var remoteVersion = new CompletableFuture<>();
|
||||||
|
var ping = Packets.PING.newPacket()
|
||||||
|
.setBukkit();
|
||||||
|
locks.put(ping.getId(), remoteVersion);
|
||||||
|
ping.send(connection);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var o = remoteVersion.get();
|
||||||
|
if (!(o instanceof PingPacket packet))
|
||||||
|
throw new IllegalStateException("Invalid response from remote server");
|
||||||
|
if (!packet.getVersion().contains(ping.getVersion().get(0)))
|
||||||
|
throw new IllegalStateException("Remote server version does not match");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
connection.disconnect();
|
||||||
|
throw new IllegalStateException("Failed to connect to remote server", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log.info(name + ": Uploading pack...");
|
||||||
|
iterate(engine.getData().getDataFolder(), f -> {
|
||||||
|
if (exit.get()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
limited.acquire();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
exit.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiBurst.burst.complete(() -> {
|
||||||
|
try {
|
||||||
|
upload(exit, base, f, studio, 8192);
|
||||||
|
} finally {
|
||||||
|
limited.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
limited.acquireAll();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
|
||||||
|
log.info(name + ": Done uploading pack");
|
||||||
|
log.info(name + ": Initializing Engine...");
|
||||||
|
var future = new CompletableFuture<>();
|
||||||
|
var packet = Packets.ENGINE.newPacket()
|
||||||
|
.setDimension(engine.getDimension().getLoadKey())
|
||||||
|
.setSeed(engine.getWorld().getRawWorldSeed())
|
||||||
|
.setRadius(engine.getMantle().getRadius());
|
||||||
|
|
||||||
|
locks.put(packet.getId(), future);
|
||||||
|
packet.send(connection);
|
||||||
|
|
||||||
|
try {
|
||||||
|
future.get();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
log.info(name + ": Done initializing Engine");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upload(AtomicBoolean exit, File base, File f, boolean studio, int packetSize) {
|
||||||
|
if (exit.get() || (!studio && !f.getAbsolutePath().startsWith(base.getAbsolutePath())))
|
||||||
|
return;
|
||||||
|
|
||||||
|
String path = studio ? "iris/pack/" : "";
|
||||||
|
path += f.getAbsolutePath().substring(base.getAbsolutePath().length() + 1);
|
||||||
|
|
||||||
|
try (FileInputStream in = new FileInputStream(f)) {
|
||||||
|
long offset = 0;
|
||||||
|
byte[] data;
|
||||||
|
while ((data = in.readNBytes(packetSize)).length > 0 && !exit.get()) {
|
||||||
|
var future = new CompletableFuture<>();
|
||||||
|
var packet = Packets.FILE.newPacket()
|
||||||
|
.setPath(path)
|
||||||
|
.setOffset(offset)
|
||||||
|
.setLength(f.length())
|
||||||
|
.setData(data);
|
||||||
|
|
||||||
|
locks.put(packet.getId(), future);
|
||||||
|
packet.send(connection);
|
||||||
|
future.get();
|
||||||
|
|
||||||
|
offset += data.length;
|
||||||
|
}
|
||||||
|
} catch (IOException | ExecutionException | InterruptedException e) {
|
||||||
|
Iris.error("Failed to upload " + f);
|
||||||
|
e.printStackTrace();
|
||||||
|
exit.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void iterate(File file, Consumer<File> consumer) {
|
||||||
|
var queue = new ArrayDeque<File>();
|
||||||
|
queue.add(file);
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
var f = queue.remove();
|
||||||
|
if (f.isFile())
|
||||||
|
consumer.accept(f);
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
var files = f.listFiles();
|
||||||
|
if (files == null) continue;
|
||||||
|
queue.addAll(Arrays.asList(files));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
close0();
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRegions(int x, int z, PregenListener listener) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateRegion(int x, int z, PregenListener listener) {
|
||||||
|
var semaphore = future.join();
|
||||||
|
try {
|
||||||
|
semaphore.acquire();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
semaphore.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = Packets.PREGEN.newPacket()
|
||||||
|
.setX(x)
|
||||||
|
.setZ(z);
|
||||||
|
new PregenHolder(p, engine.getMantle().getRadius(), true, listener)
|
||||||
|
.put(holders);
|
||||||
|
p.send(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateChunk(int x, int z, PregenListener listener) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethod(int x, int z) {
|
||||||
|
return "Cloud";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mantle getMantle() {
|
||||||
|
return engine.getMantle().getMantle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public World getWorld() {
|
||||||
|
return engine.getWorld().realWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPacket(Packet raw) throws Exception {
|
||||||
|
if (raw instanceof ChunkPacket packet) {
|
||||||
|
headless.addChunk(packet);
|
||||||
|
holders.get(packet.getPregenId())
|
||||||
|
.getListener()
|
||||||
|
.onChunkGenerated(packet.getX(), packet.getZ());
|
||||||
|
} else if (raw instanceof MantleChunkPacket packet) {
|
||||||
|
if (holders.get(packet.getPregenId())
|
||||||
|
.remove(packet.getX(), packet.getZ())) {
|
||||||
|
future.join().release();
|
||||||
|
}
|
||||||
|
packet.set(getMantle());
|
||||||
|
} else if (raw instanceof MantleChunkPacket.Request packet) {
|
||||||
|
var mantle = getMantle();
|
||||||
|
for (var chunk : packet.getPositions()) {
|
||||||
|
Packets.MANTLE_CHUNK.newPacket()
|
||||||
|
.setPregenId(packet.getPregenId())
|
||||||
|
.read(chunk, mantle)
|
||||||
|
.send(connection);
|
||||||
|
}
|
||||||
|
} else if (raw instanceof InfoPacket packet) {
|
||||||
|
if (packet.getNodeCount() > 0 && !future.isDone())
|
||||||
|
future.complete(new LimitedSemaphore(packet.getNodeCount()));
|
||||||
|
//if (packet.getCps() >= 0)
|
||||||
|
// Iris.info("Cloud CPS: " + packet.getCps());
|
||||||
|
} else if (raw instanceof DonePacket packet) {
|
||||||
|
locks.remove(packet.getId()).complete(null);
|
||||||
|
} else if (raw instanceof PingPacket packet) {
|
||||||
|
locks.remove(packet.getId()).complete(packet);
|
||||||
|
} else if (raw instanceof ErrorPacket packet) {
|
||||||
|
packet.log(log, Level.SEVERE);
|
||||||
|
} else throw new RejectedException("Unhandled packet: " + raw.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect() {
|
||||||
|
try {
|
||||||
|
if (!future.isDone())
|
||||||
|
future.cancel(false);
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
PregeneratorJob.shutdownInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close0() {
|
||||||
|
if (!future.isCancelled()) {
|
||||||
|
try {
|
||||||
|
future.join().acquireAll();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
headless.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.log(Level.SEVERE, "Failed to close headless", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.volmit.iris.server.pregen;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenTask;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import com.volmit.iris.util.math.Spiraled;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@Builder(builderMethodName = "couldBuilder")
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class CloudTask extends PregenTask {
|
||||||
|
@Builder.Default
|
||||||
|
private boolean resetCache = false;
|
||||||
|
@Builder.Default
|
||||||
|
private boolean gui = false;
|
||||||
|
@Builder.Default
|
||||||
|
private Position2 center = new Position2(0, 0);
|
||||||
|
@Builder.Default
|
||||||
|
private int width = 1;
|
||||||
|
@Builder.Default
|
||||||
|
private int height = 1;
|
||||||
|
private int distance;
|
||||||
|
|
||||||
|
private CloudTask(boolean resetCache, boolean gui, Position2 center, int width, int height, int distance) {
|
||||||
|
super(resetCache, gui, center, width, height);
|
||||||
|
this.resetCache = resetCache;
|
||||||
|
this.gui = gui;
|
||||||
|
this.center = center;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
int d = distance & 31;
|
||||||
|
if (d > 0) d = 32 - d;
|
||||||
|
this.distance = 32 + d + distance >> 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void iterateRegions(Spiraled s) {
|
||||||
|
var c = Comparator.comparingInt(DPos2::distance);
|
||||||
|
for (int oX = 0; oX < distance; oX++) {
|
||||||
|
for (int oZ = 0; oZ < distance; oZ++) {
|
||||||
|
var p = new KList<DPos2>();
|
||||||
|
for (int x = -width; x <= width - oX; x+=distance) {
|
||||||
|
for (int z = -height; z <= height - oZ; z+=distance) {
|
||||||
|
s.on(x + oX, z + oZ);
|
||||||
|
//p.add(new DPos2(x + oX, z + oZ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.sort(c);
|
||||||
|
p.forEach(i -> i.on(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record DPos2(int x, int z, int distance) {
|
||||||
|
private DPos2(int x, int z) {
|
||||||
|
this(x, z, x * x + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void on(Spiraled s) {
|
||||||
|
s.on(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class ByteBufUtil {
|
||||||
|
|
||||||
|
public static String readString(ByteBuf byteBuf) {
|
||||||
|
return new String(readBytes(byteBuf), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeString(ByteBuf byteBuf, String s) {
|
||||||
|
writeBytes(byteBuf, s.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytes(ByteBuf byteBuf) {
|
||||||
|
byte[] bytes = new byte[byteBuf.readInt()];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeBytes(ByteBuf byteBuf, byte[] bytes) {
|
||||||
|
byteBuf.writeInt(bytes.length);
|
||||||
|
byteBuf.writeBytes(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
import com.volmit.iris.server.packet.Packets;
|
||||||
|
import com.volmit.iris.util.math.M;
|
||||||
|
import com.volmit.iris.util.math.RollingSequence;
|
||||||
|
import com.volmit.iris.util.scheduling.Looper;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
public class CPSLooper extends Looper {
|
||||||
|
private final RollingSequence chunksPerSecond = new RollingSequence(10);
|
||||||
|
private final AtomicInteger generated = new AtomicInteger();
|
||||||
|
private final AtomicInteger generatedLast = new AtomicInteger();
|
||||||
|
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||||
|
private final IrisConnection connection;
|
||||||
|
private int nodeCount = 0;
|
||||||
|
|
||||||
|
public CPSLooper(String name, IrisConnection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
setName(name);
|
||||||
|
setPriority(Thread.MAX_PRIORITY);
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChunks(int count) {
|
||||||
|
generated.addAndGet(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exit() {
|
||||||
|
running.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setNodeCount(int count) {
|
||||||
|
if (nodeCount != 0 || count < 1)
|
||||||
|
return;
|
||||||
|
nodeCount = count;
|
||||||
|
Packets.INFO.newPacket()
|
||||||
|
.setNodeCount(nodeCount)
|
||||||
|
.send(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long loop() {
|
||||||
|
if (!running.get())
|
||||||
|
return -1;
|
||||||
|
long t = M.ms();
|
||||||
|
|
||||||
|
int secondGenerated = generated.get() - generatedLast.get();
|
||||||
|
generatedLast.set(generated.get());
|
||||||
|
chunksPerSecond.put(secondGenerated);
|
||||||
|
|
||||||
|
if (secondGenerated > 0 && nodeCount > 0) {
|
||||||
|
Packets.INFO.newPacket()
|
||||||
|
.setNodeCount(nodeCount)
|
||||||
|
.setCps((int) Math.round(chunksPerSecond.getAverage()))
|
||||||
|
.setGenerated(secondGenerated)
|
||||||
|
.send(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(5000 - (M.ms() - t), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.IrisConnection;
|
||||||
|
|
||||||
|
public interface ConnectionHolder {
|
||||||
|
|
||||||
|
IrisConnection getConnection();
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ErrorPacket implements Packet {
|
||||||
|
private String message;
|
||||||
|
private String stackTrace;
|
||||||
|
|
||||||
|
public ErrorPacket(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorPacket(String message, Throwable cause) {
|
||||||
|
this.message = message;
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
cause.printStackTrace(new PrintWriter(writer));
|
||||||
|
stackTrace = writer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuf byteBuf) throws IOException {
|
||||||
|
message = ByteBufUtil.readString(byteBuf);
|
||||||
|
if (byteBuf.readBoolean()) {
|
||||||
|
stackTrace = ByteBufUtil.readString(byteBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf byteBuf) throws IOException {
|
||||||
|
ByteBufUtil.writeString(byteBuf, message);
|
||||||
|
byteBuf.writeBoolean(stackTrace != null);
|
||||||
|
if (stackTrace != null) {
|
||||||
|
ByteBufUtil.writeString(byteBuf, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(Logger logger, Level level) {
|
||||||
|
if (stackTrace == null) {
|
||||||
|
logger.log(level, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.log(level, message + "\n" + stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class LimitedSemaphore extends Semaphore {
|
||||||
|
private final int permits;
|
||||||
|
|
||||||
|
public LimitedSemaphore(int permits) {
|
||||||
|
super(permits);
|
||||||
|
this.permits = permits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runBlocking(Runnable runnable) throws InterruptedException {
|
||||||
|
try {
|
||||||
|
acquire();
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runAllBlocking(Runnable runnable) throws InterruptedException {
|
||||||
|
try {
|
||||||
|
acquireAll();
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
releaseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void acquireAll() throws InterruptedException {
|
||||||
|
acquire(permits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releaseAll() {
|
||||||
|
release(permits);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
|
||||||
|
public interface PacketListener {
|
||||||
|
|
||||||
|
void onPacket(Packet packet) throws Exception;
|
||||||
|
|
||||||
|
default void onDisconnect() {}
|
||||||
|
|
||||||
|
default boolean isAccepting() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import com.volmit.iris.server.packet.Packet;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public interface PacketSendListener {
|
||||||
|
static PacketSendListener thenRun(Runnable runnable) {
|
||||||
|
return new PacketSendListener() {
|
||||||
|
public void onSuccess() {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Packet onFailure() {
|
||||||
|
runnable.run();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onSuccess() {}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
default Packet onFailure() { return null; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.volmit.iris.server.util;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.server.packet.work.PregenPacket;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class PregenHolder {
|
||||||
|
private final UUID id;
|
||||||
|
private final int x, z, r;
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final KList<Position2> chunks = new KList<>();
|
||||||
|
private final PregenListener listener;
|
||||||
|
|
||||||
|
public PregenHolder(PregenPacket packet, int r, boolean fill, PregenListener listener) {
|
||||||
|
this.id = packet.getId();
|
||||||
|
this.x = packet.getX();
|
||||||
|
this.z = packet.getZ();
|
||||||
|
this.r = r;
|
||||||
|
this.listener = listener;
|
||||||
|
|
||||||
|
if (fill)
|
||||||
|
iterate(chunks::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(KMap<UUID, PregenHolder> holders) {
|
||||||
|
holders.put(id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean remove(int x, int z) {
|
||||||
|
chunks.remove(new Position2(x, z));
|
||||||
|
boolean b = chunks.isEmpty();
|
||||||
|
if (b && listener != null) listener.onRegionGenerated(x, z);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void iterate(Consumer<Position2> consumer) {
|
||||||
|
int cX = x << 5;
|
||||||
|
int cZ = z << 5;
|
||||||
|
for (int x = -r; x <= r; x++) {
|
||||||
|
for (int z = -r; z <= r; z++) {
|
||||||
|
if (x == 0 && z == 0) {
|
||||||
|
for (int xx = 0; xx < 32; xx++) {
|
||||||
|
for (int zz = 0; zz < 32; zz++) {
|
||||||
|
consumer.accept(new Position2(x + cX + xx, z + cZ + zz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumer.accept(new Position2(x + cX + x < 0 ? 0 : 32, z + cZ + z < 0 ? 0 : 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ package com.volmit.iris.util.data;
|
|||||||
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.math.Direction;
|
import com.volmit.iris.util.math.Direction;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||||
@@ -651,6 +652,10 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
|
|||||||
return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2);
|
return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterator<Block> chunkedIterator() {
|
||||||
|
return new ChunkedCuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
@@ -746,4 +751,82 @@ public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ChunkedCuboidIterator implements Iterator<Block> {
|
||||||
|
private final World w;
|
||||||
|
private final int minRX, minY, minRZ, maxRX, maxY, maxRZ;
|
||||||
|
private final int minCX, minCZ, maxCX, maxCZ;
|
||||||
|
private int mX, mZ, bX, rX, rZ, y;
|
||||||
|
|
||||||
|
private Position2 chunk;
|
||||||
|
private int cX, cZ;
|
||||||
|
|
||||||
|
public ChunkedCuboidIterator(World w, int x1, int y1, int z1, int x2, int y2, int z2) {
|
||||||
|
this.w = w;
|
||||||
|
minY = Math.min(y1, y2);
|
||||||
|
maxY = Math.max(y1, y2);
|
||||||
|
int minX = Math.min(x1, x2);
|
||||||
|
int minZ = Math.min(z1, z2);
|
||||||
|
int maxX = Math.max(x1, x2);
|
||||||
|
int maxZ = Math.max(z1, z2);
|
||||||
|
minRX = minX & 15;
|
||||||
|
minRZ = minZ & 15;
|
||||||
|
maxRX = maxX & 15;
|
||||||
|
maxRZ = maxZ & 15;
|
||||||
|
|
||||||
|
minCX = minX >> 4;
|
||||||
|
minCZ = minZ >> 4;
|
||||||
|
maxCX = maxX >> 4;
|
||||||
|
maxCZ = maxZ >> 4;
|
||||||
|
cX = minCX;
|
||||||
|
cZ = minCZ;
|
||||||
|
|
||||||
|
rX = minX & 15;
|
||||||
|
rZ = minZ & 15;
|
||||||
|
y = minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return chunk != null || hasNextChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNextChunk() {
|
||||||
|
return cX <= maxCX && cZ <= maxCZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block next() {
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = new Position2(cX, cZ);
|
||||||
|
if (++cX > maxCX) {
|
||||||
|
cX = minCX;
|
||||||
|
cZ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
mX = chunk.getX() == maxCX ? maxRX : 15;
|
||||||
|
mZ = chunk.getZ() == maxCZ ? maxRZ : 15;
|
||||||
|
rX = bX = chunk.getX() == minCX ? minRX : 0;
|
||||||
|
rZ = chunk.getZ() == minCZ ? minRZ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = w.getBlockAt((chunk.getX() << 4) + rX, y, (chunk.getZ() << 4) + rZ);
|
||||||
|
if (++y >= maxY) {
|
||||||
|
y = minY;
|
||||||
|
if (++rX > mX) {
|
||||||
|
if (++rZ > mZ) {
|
||||||
|
chunk = null;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
rX = bX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -195,6 +195,11 @@ public class Mantle {
|
|||||||
return get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31);
|
return get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public void setChunk(int x, int z, MantleChunk chunk) {
|
||||||
|
get(x >> 5, z >> 5).set(x & 31, z & 31, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag or unflag a chunk
|
* Flag or unflag a chunk
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ public class MantleChunk {
|
|||||||
return flags.get(flag.ordinal()) == 1;
|
return flags.get(flag.ordinal()) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSectionHeight() {
|
||||||
|
return sections.length();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a section exists (same as get(section) != null)
|
* Check if a section exists (same as get(section) != null)
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -158,6 +158,20 @@ public class TectonicPlate {
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a tectonic plate
|
||||||
|
*
|
||||||
|
* @param x the chunk relative x (0-31)
|
||||||
|
* @param z the chunk relative z (0-31)
|
||||||
|
* @param chunk the chunk
|
||||||
|
*/
|
||||||
|
@ChunkCoordinates
|
||||||
|
public void set(int x, int z, MantleChunk chunk) {
|
||||||
|
if (x != chunk.getX() || z != chunk.getZ())
|
||||||
|
throw new IllegalArgumentException("X/Z of chunk must match the plate");
|
||||||
|
chunks.set(index(x, z), chunk);
|
||||||
|
}
|
||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
private int index(int x, int z) {
|
private int index(int x, int z) {
|
||||||
return Cache.to1D(x, z, 0, 32, 32);
|
return Cache.to1D(x, z, 0, 32, 32);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ libraries:
|
|||||||
- rhino:js:1.7R2
|
- rhino:js:1.7R2
|
||||||
- bsf:bsf:2.4.0
|
- bsf:bsf:2.4.0
|
||||||
- org.lz4:lz4-java:1.8.0
|
- org.lz4:lz4-java:1.8.0
|
||||||
|
- io.netty:netty-all:4.1.112.Final
|
||||||
commands:
|
commands:
|
||||||
iris:
|
iris:
|
||||||
aliases: [ ir, irs ]
|
aliases: [ ir, irs ]
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import com.volmit.iris.engine.framework.Engine;
|
|||||||
import com.volmit.iris.engine.framework.EngineStage;
|
import com.volmit.iris.engine.framework.EngineStage;
|
||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||||
import com.volmit.iris.engine.object.IrisBiome;
|
import com.volmit.iris.engine.object.IrisBiome;
|
||||||
|
import com.volmit.iris.server.node.IrisSession;
|
||||||
|
import com.volmit.iris.server.packet.work.ChunkPacket;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.context.ChunkContext;
|
import com.volmit.iris.util.context.ChunkContext;
|
||||||
@@ -61,6 +63,7 @@ import net.minecraft.world.level.chunk.storage.RegionFile;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -82,6 +85,8 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
private final RNG BIOME_RNG;
|
private final RNG BIOME_RNG;
|
||||||
private final @Getter int minBuildHeight;
|
private final @Getter int minBuildHeight;
|
||||||
private final @Getter int height;
|
private final @Getter int height;
|
||||||
|
private IrisSession session;
|
||||||
|
private CompletingThread regionThread;
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
|
||||||
public Headless(NMSBinding binding, Engine engine) {
|
public Headless(NMSBinding binding, Engine engine) {
|
||||||
@@ -127,6 +132,12 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
cleaner.start();
|
cleaner.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSession(IrisSession session) {
|
||||||
|
if (this.session != null)
|
||||||
|
throw new IllegalStateException("Session already set");
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getLoadedChunks() {
|
public int getLoadedChunks() {
|
||||||
return loadedChunks.get();
|
return loadedChunks.get();
|
||||||
@@ -153,21 +164,42 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateRegion(MultiBurst burst, int x, int z, PregenListener listener) {
|
public synchronized CompletableFuture<Void> generateRegion(MultiBurst burst, int x, int z, int maxConcurrent, PregenListener listener) {
|
||||||
if (closed) return;
|
if (closed) return CompletableFuture.completedFuture(null);
|
||||||
boolean listening = listener != null;
|
if (regionThread != null && !regionThread.future.isDone())
|
||||||
if (listening) listener.onRegionGenerating(x, z);
|
throw new IllegalStateException("Region generation already in progress");
|
||||||
CountDownLatch latch = new CountDownLatch(1024);
|
|
||||||
iterateRegion(x, z, pos -> burst.complete(() -> {
|
regionThread = new CompletingThread(() -> {
|
||||||
if (listening) listener.onChunkGenerating(pos.x, pos.z);
|
boolean listening = listener != null;
|
||||||
generateChunk(pos.x, pos.z);
|
Semaphore semaphore = new Semaphore(maxConcurrent);
|
||||||
if (listening) listener.onChunkGenerated(pos.x, pos.z);
|
CountDownLatch latch = new CountDownLatch(1024);
|
||||||
latch.countDown();
|
|
||||||
}));
|
iterateRegion(x, z, pos -> {
|
||||||
try {
|
try {
|
||||||
latch.await();
|
semaphore.acquire();
|
||||||
} catch (InterruptedException ignored) {}
|
} catch (InterruptedException e) {
|
||||||
if (listening) listener.onRegionGenerated(x, z);
|
semaphore.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
burst.complete(() -> {
|
||||||
|
try {
|
||||||
|
if (listening) listener.onChunkGenerating(pos.x, pos.z);
|
||||||
|
generateChunk(pos.x, pos.z);
|
||||||
|
if (listening) listener.onChunkGenerated(pos.x, pos.z);
|
||||||
|
} finally {
|
||||||
|
semaphore.release();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
if (listening) listener.onRegionGenerated(x, z);
|
||||||
|
}, "Region Generator - " + x + "," + z, Thread.MAX_PRIORITY);
|
||||||
|
|
||||||
|
return regionThread.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RegionCoordinates
|
@RegionCoordinates
|
||||||
@@ -199,6 +231,12 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
inject(engine, chunk, ctx);
|
inject(engine, chunk, ctx);
|
||||||
chunk.setStatus(ChunkStatus.FULL);
|
chunk.setStatus(ChunkStatus.FULL);
|
||||||
|
|
||||||
|
if (session != null) {
|
||||||
|
session.completeChunk(x, z, write(chunk));
|
||||||
|
loadedChunks.decrementAndGet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
long key = Cache.key(pos.getRegionX(), pos.getRegionZ());
|
long key = Cache.key(pos.getRegionX(), pos.getRegionZ());
|
||||||
regions.computeIfAbsent(key, Region::new)
|
regions.computeIfAbsent(key, Region::new)
|
||||||
.add(chunk);
|
.add(chunk);
|
||||||
@@ -209,6 +247,16 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChunk(ChunkPacket packet) {
|
||||||
|
if (closed) return;
|
||||||
|
if (session != null) throw new IllegalStateException("Headless running as Server");
|
||||||
|
var pos = new ChunkPos(packet.getX(), packet.getZ());
|
||||||
|
regions.computeIfAbsent(Cache.key(pos.getRegionX(), pos.getRegionZ()), Region::new)
|
||||||
|
.add(packet);
|
||||||
|
loadedChunks.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
@BlockCoordinates
|
@BlockCoordinates
|
||||||
private ChunkContext generate(Engine engine, int x, int z, Hunk<BlockData> vblocks, Hunk<org.bukkit.block.Biome> vbiomes) throws WrongEngineBroException {
|
private ChunkContext generate(Engine engine, int x, int z, Hunk<BlockData> vblocks, Hunk<org.bukkit.block.Biome> vbiomes) throws WrongEngineBroException {
|
||||||
if (engine.isClosed()) {
|
if (engine.isClosed()) {
|
||||||
@@ -284,6 +332,11 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (closed) return;
|
if (closed) return;
|
||||||
try {
|
try {
|
||||||
|
if (regionThread != null) {
|
||||||
|
regionThread.future.join();
|
||||||
|
regionThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
regions.values().forEach(Region::submit);
|
regions.values().forEach(Region::submit);
|
||||||
Iris.info("Waiting for " + loadedChunks.get() + " chunks to unload...");
|
Iris.info("Waiting for " + loadedChunks.get() + " chunks to unload...");
|
||||||
while (loadedChunks.get() > 0 || !regions.isEmpty())
|
while (loadedChunks.get() > 0 || !regions.isEmpty())
|
||||||
@@ -304,6 +357,7 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
private final int x, z;
|
private final int x, z;
|
||||||
private final long key;
|
private final long key;
|
||||||
private final KList<ProtoChunk> chunks = new KList<>(1024);
|
private final KList<ProtoChunk> chunks = new KList<>(1024);
|
||||||
|
private final KList<ChunkPacket> remoteChunks = new KList<>(1024);
|
||||||
private final AtomicBoolean full = new AtomicBoolean();
|
private final AtomicBoolean full = new AtomicBoolean();
|
||||||
private long lastEntry = M.ms();
|
private long lastEntry = M.ms();
|
||||||
|
|
||||||
@@ -334,13 +388,31 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
}
|
}
|
||||||
loadedChunks.decrementAndGet();
|
loadedChunks.decrementAndGet();
|
||||||
}
|
}
|
||||||
|
for (var chunk : remoteChunks) {
|
||||||
|
var pos = new ChunkPos(chunk.getX(), chunk.getZ());
|
||||||
|
try (DataOutputStream dos = regionFile.getChunkDataOutputStream(pos)) {
|
||||||
|
dos.write(chunk.getData());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to save remote chunk " + pos.x + ", " + pos.z);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
loadedChunks.decrementAndGet();
|
||||||
|
}
|
||||||
regions.remove(key);
|
regions.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void add(ProtoChunk chunk) {
|
public synchronized void add(ProtoChunk chunk) {
|
||||||
chunks.add(chunk);
|
chunks.add(chunk);
|
||||||
lastEntry = M.ms();
|
lastEntry = M.ms();
|
||||||
if (chunks.size() < 1024)
|
if (chunks.size() + remoteChunks.size() < 1024)
|
||||||
|
return;
|
||||||
|
submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void add(ChunkPacket packet) {
|
||||||
|
remoteChunks.add(packet);
|
||||||
|
lastEntry = M.ms();
|
||||||
|
if (chunks.size() + remoteChunks.size() < 1024)
|
||||||
return;
|
return;
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
@@ -350,4 +422,31 @@ public class Headless implements IHeadless, LevelHeightAccessor {
|
|||||||
executor.submit(this);
|
executor.submit(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] write(ProtoChunk chunk) throws IOException {
|
||||||
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(out)) {
|
||||||
|
NbtIo.write(binding.serializeChunk(chunk, Headless.this), dos);
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompletingThread extends Thread {
|
||||||
|
private final CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
|
||||||
|
private CompletingThread(Runnable task, String name, int priority) {
|
||||||
|
super(task, name);
|
||||||
|
setPriority(priority);
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
super.run();
|
||||||
|
} finally {
|
||||||
|
future.complete(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user