mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-02-16 18:40:41 +00:00
Compare commits
39 Commits
3.8.0-1.20
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41888e33f6 | ||
|
|
25fa2553e5 | ||
|
|
86f78baecf | ||
|
|
c31158578f | ||
|
|
4e86d7d634 | ||
|
|
62fe29cf34 | ||
|
|
a3bcea4f3e | ||
|
|
7befce1084 | ||
|
|
351a1aa495 | ||
|
|
d2cb8a9032 | ||
|
|
509715087c | ||
|
|
12527ecdd8 | ||
|
|
01a2999e03 | ||
|
|
dbafe84fa5 | ||
|
|
40b020fc5d | ||
|
|
d15f7d62d1 | ||
|
|
f9c062c794 | ||
|
|
6a89b8bd06 | ||
|
|
194fcb2ea7 | ||
|
|
3b68f855b2 | ||
|
|
cfbf68d37a | ||
|
|
9999f3e429 | ||
|
|
97fa0562a4 | ||
|
|
3abe671851 | ||
|
|
e164a3bb5c | ||
|
|
4dfb4441e4 | ||
|
|
e686f67453 | ||
|
|
e2eed4812a | ||
|
|
1737833bfe | ||
|
|
a4c5f46c37 | ||
|
|
025fa833c4 | ||
|
|
98cc82cc3d | ||
|
|
298365f588 | ||
|
|
90e5720e2e | ||
|
|
7cd43791f4 | ||
|
|
a251d192ad | ||
|
|
abfff28f43 | ||
|
|
fbdae4c928 | ||
|
|
526efd3ae1 |
@@ -1,4 +1,5 @@
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import xyz.jpenilla.runpaper.task.RunServer
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@@ -33,7 +34,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "com.volmit"
|
||||
version = "3.8.0-1.20.1-1.21.10"
|
||||
version = "3.9.1-1.20.1-1.21.11"
|
||||
|
||||
apply<ApiGenerator>()
|
||||
|
||||
@@ -50,7 +51,7 @@ registerCustomOutputTask("PixelFury", "C://Users/repix/workplace/Iris/1.21.3 - D
|
||||
registerCustomOutputTask("PixelFuryDev", "C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins")
|
||||
// ========================== UNIX ==============================
|
||||
registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins")
|
||||
registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/Server/plugins")
|
||||
registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/plugins")
|
||||
registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins")
|
||||
registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins")
|
||||
// ==============================================================
|
||||
@@ -60,19 +61,20 @@ val serverMaxHeap = "10G"
|
||||
val additionalFlags = "-XX:+AlwaysPreTouch"
|
||||
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
|
||||
val color = "truecolor"
|
||||
val errorReporting = findProperty("errorReporting") as Boolean? ?: false
|
||||
val errorReporting = "true" == findProperty("errorReporting")
|
||||
|
||||
val nmsBindings = mapOf(
|
||||
"v1_21_R6" to "1.21.10-R0.1-SNAPSHOT",
|
||||
"v1_21_R5" to "1.21.8-R0.1-SNAPSHOT",
|
||||
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
|
||||
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
|
||||
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
|
||||
"v1_21_R1" to "1.21.1-R0.1-SNAPSHOT",
|
||||
"v1_20_R4" to "1.20.6-R0.1-SNAPSHOT",
|
||||
"v1_20_R3" to "1.20.4-R0.1-SNAPSHOT",
|
||||
"v1_20_R2" to "1.20.2-R0.1-SNAPSHOT",
|
||||
"v1_20_R1" to "1.20.1-R0.1-SNAPSHOT",
|
||||
"v1_21_R7" to "1.21.11-R0.1-SNAPSHOT",
|
||||
"v1_21_R6" to "1.21.10-R0.1-SNAPSHOT",
|
||||
"v1_21_R5" to "1.21.8-R0.1-SNAPSHOT",
|
||||
"v1_21_R4" to "1.21.5-R0.1-SNAPSHOT",
|
||||
"v1_21_R3" to "1.21.4-R0.1-SNAPSHOT",
|
||||
"v1_21_R2" to "1.21.3-R0.1-SNAPSHOT",
|
||||
"v1_21_R1" to "1.21.1-R0.1-SNAPSHOT",
|
||||
"v1_20_R4" to "1.20.6-R0.1-SNAPSHOT",
|
||||
"v1_20_R3" to "1.20.4-R0.1-SNAPSHOT",
|
||||
"v1_20_R2" to "1.20.2-R0.1-SNAPSHOT",
|
||||
"v1_20_R1" to "1.20.1-R0.1-SNAPSHOT",
|
||||
)
|
||||
val jvmVersion = mapOf<String, Int>()
|
||||
nmsBindings.forEach { (key, value) ->
|
||||
@@ -109,19 +111,21 @@ nmsBindings.forEach { (key, value) ->
|
||||
}
|
||||
}
|
||||
|
||||
val included: Configuration by configurations.creating
|
||||
val jarJar: Configuration by configurations.creating
|
||||
dependencies {
|
||||
for (key in nmsBindings.keys) {
|
||||
implementation(project(":nms:$key", "reobf"))
|
||||
included(project(":nms:$key", "reobf"))
|
||||
}
|
||||
implementation(project(":core", "shadow"))
|
||||
included(project(":core", "shadow"))
|
||||
jarJar(project(":core:agent"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
jar {
|
||||
inputs.files(included)
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from(jarJar, configurations.runtimeClasspath.map { it.resolve().map(::zipTree) })
|
||||
from(jarJar, provider { included.resolve().map(::zipTree) })
|
||||
archiveFileName.set("Iris-${project.version}.jar")
|
||||
}
|
||||
|
||||
@@ -184,6 +188,12 @@ configurations.configureEach {
|
||||
allprojects {
|
||||
apply<JavaPlugin>()
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
@@ -231,14 +241,14 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
if (JavaVersion.current().toString() != "21") {
|
||||
if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) {
|
||||
System.err.println()
|
||||
System.err.println("=========================================================================================================")
|
||||
System.err.println("You must run gradle on Java 21. You are using " + JavaVersion.current())
|
||||
System.err.println("You must run gradle on Java 21 or newer. You are using " + JavaVersion.current())
|
||||
System.err.println()
|
||||
System.err.println("=== For IDEs ===")
|
||||
System.err.println("1. Configure the project for Java 21")
|
||||
System.err.println("2. Configure the bundled gradle to use Java 21 in settings")
|
||||
System.err.println("1. Configure the project for Java 21 toolchain")
|
||||
System.err.println("2. Configure the bundled gradle to use Java 21+ in settings")
|
||||
System.err.println()
|
||||
System.err.println("=== For Command Line (gradlew) ===")
|
||||
System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html")
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version embeddedKotlinVersion
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_21)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
@@ -13,4 +29,4 @@ dependencies {
|
||||
implementation("com.github.VolmitSoftware:NMSTools:c88961416f")
|
||||
implementation("io.papermc.paperweight:paperweight-userdev:2.0.0-beta.18")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import io.github.slimjar.func.slimjarHelper
|
||||
import io.github.slimjar.resolver.data.Mirror
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import java.net.URI
|
||||
|
||||
/*
|
||||
@@ -112,10 +114,20 @@ dependencies {
|
||||
|
||||
java {
|
||||
disableAutoTargetJvm()
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_21)
|
||||
}
|
||||
}
|
||||
|
||||
sentry {
|
||||
url = "http://sentry.volmit.com:8080/"
|
||||
url = "http://sentry.volmit.com:8080"
|
||||
autoInstallation.enabled = false
|
||||
includeSourceContext = true
|
||||
|
||||
@@ -175,20 +187,28 @@ tasks {
|
||||
relocate("io.github.slimjar", "$lib.slimjar")
|
||||
exclude("modules/loader-agent.isolated-jar")
|
||||
}
|
||||
|
||||
sentryCollectSourcesJava {
|
||||
dependsOn(generateTemplates)
|
||||
}
|
||||
}
|
||||
|
||||
val templateSource = file("src/main/templates")
|
||||
val templateDest = layout.buildDirectory.dir("generated/sources/templates")
|
||||
val templateDest = layout.buildDirectory.dir("generated/sources/templates")!!
|
||||
val generateTemplates = tasks.register<Copy>("generateTemplates") {
|
||||
inputs.properties(
|
||||
"environment" to if (project.hasProperty("release")) "production" else "development",
|
||||
"environment" to when {
|
||||
project.hasProperty("release") -> "production"
|
||||
project.hasProperty("argghh") -> "Argghh!"
|
||||
else -> "development"
|
||||
},
|
||||
"commit" to provider {
|
||||
val res = runCatching { project.extensions.getByType<Grgit>().head().id }
|
||||
res.getOrDefault("")
|
||||
.takeIf { it.length == 40 } ?: {
|
||||
logger.error("Git commit hash not found", res.exceptionOrNull())
|
||||
.takeIf { it.length == 40 } ?: run {
|
||||
this.logger.error("Git commit hash not found", res.exceptionOrNull())
|
||||
"unknown"
|
||||
}()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -208,4 +228,4 @@ rootProject.tasks.named("prepareKotlinBuildScriptModel") {
|
||||
|
||||
sourceSets.main {
|
||||
java.srcDir(generateTemplates.map { it.outputs })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.volmit.iris.core.link.IrisPapiExpansion;
|
||||
import com.volmit.iris.core.link.MultiverseCoreLink;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.nms.INMS;
|
||||
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
|
||||
import com.volmit.iris.core.pregenerator.LazyPregenerator;
|
||||
import com.volmit.iris.core.service.StudioSVC;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
@@ -43,7 +42,6 @@ import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.exceptions.IrisException;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.function.NastyRunnable;
|
||||
import com.volmit.iris.util.io.FileWatcher;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
@@ -53,7 +51,6 @@ import com.volmit.iris.util.math.M;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.misc.Bindings;
|
||||
import com.volmit.iris.util.misc.SlimJar;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.plugin.IrisService;
|
||||
import com.volmit.iris.util.plugin.VolmitPlugin;
|
||||
@@ -84,9 +81,6 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
|
||||
import static com.volmit.iris.core.safeguard.ServerBootSFG.passedserversoftware;
|
||||
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
public class Iris extends VolmitPlugin implements Listener {
|
||||
private static final Queue<Runnable> syncJobs = new ShurikenQueue<>();
|
||||
@@ -308,9 +302,6 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
public static void info(String format, Object... args) {
|
||||
msg(C.WHITE + String.format(format, args));
|
||||
}
|
||||
public static void safeguard(String format, Object... args) {
|
||||
msg(C.RESET + String.format(format, args));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void later(NastyRunnable object) {
|
||||
@@ -420,6 +411,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
pw.println();
|
||||
pw.println();
|
||||
}
|
||||
pw.println("[%%__USER__%%,%%__RESOURCE__%%,%%__PRODUCT__%%,%%__BUILTBYBIT__%%]");
|
||||
|
||||
pw.close();
|
||||
Iris.info("DUMPED! See " + fi.getAbsolutePath());
|
||||
@@ -449,9 +441,9 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
IO.delete(new File("iris"));
|
||||
compat = IrisCompat.configured(getDataFile("compat.json"));
|
||||
ServerConfigurator.configure();
|
||||
IrisSafeguard.IrisSafeguardSystem();
|
||||
IrisSafeguard.execute();
|
||||
getSender().setTag(getTag());
|
||||
IrisSafeguard.splash(true);
|
||||
IrisSafeguard.splash();
|
||||
tickets = new ChunkTickets();
|
||||
linkMultiverseCore = new MultiverseCoreLink();
|
||||
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
||||
@@ -459,7 +451,6 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
services.values().forEach(this::registerListener);
|
||||
addShutdownHook();
|
||||
J.s(() -> {
|
||||
J.a(IrisSafeguard::suggestPaper);
|
||||
J.a(() -> IO.delete(getTemp()));
|
||||
J.a(LazyPregenerator::loadLazyGenerators, 100);
|
||||
J.a(this::bstats);
|
||||
@@ -467,7 +458,6 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
J.sr(this::tickQueue, 0);
|
||||
J.s(this::setupPapi);
|
||||
J.a(ServerConfigurator::configure, 20);
|
||||
IrisSafeguard.splash(false);
|
||||
|
||||
autoStartStudio();
|
||||
checkForBukkitWorlds(s -> true);
|
||||
@@ -529,9 +519,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
Player r = new KList<>(getServer().getOnlinePlayers()).getRandom();
|
||||
Iris.service(StudioSVC.class).open(r != null ? new VolmitSender(r) : getSender(), 1337, IrisSettings.get().getGenerator().getDefaultWorldType(), (w) -> {
|
||||
J.s(() -> {
|
||||
var spawn = w.getSpawnLocation();
|
||||
for (Player i : getServer().getOnlinePlayers()) {
|
||||
i.setGameMode(GameMode.SPECTATOR);
|
||||
i.teleport(new Location(w, 0, 200, 0));
|
||||
i.teleport(spawn);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -560,10 +551,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
enable();
|
||||
super.onEnable();
|
||||
Bukkit.getPluginManager().registerEvents(this, this);
|
||||
setupChecks();
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
if (IrisSafeguard.isForceShutdown()) return;
|
||||
services.values().forEach(IrisService::onDisable);
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
HandlerList.unregisterAll((Plugin) this);
|
||||
@@ -591,49 +582,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
|
||||
@Override
|
||||
public String getTag(String subTag) {
|
||||
if (unstablemode) {
|
||||
return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.RED + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": ";
|
||||
}
|
||||
if (warningmode) {
|
||||
return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.GOLD + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": ";
|
||||
}
|
||||
return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.IRIS + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": ";
|
||||
|
||||
}
|
||||
|
||||
private boolean setupChecks() {
|
||||
boolean passed = true;
|
||||
Iris.info("Version Information: " + instance.getServer().getVersion() + " | " + instance.getServer().getBukkitVersion());
|
||||
if (INMS.get() instanceof NMSBinding1X) {
|
||||
passed = false;
|
||||
Iris.warn("============================================");
|
||||
Iris.warn("=");
|
||||
Iris.warn("=");
|
||||
Iris.warn("=");
|
||||
Iris.warn("Iris is not compatible with this version of Minecraft.");
|
||||
Iris.warn("=");
|
||||
Iris.warn("=");
|
||||
Iris.warn("=");
|
||||
Iris.warn("============================================");
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("io.papermc.paper.configuration.PaperConfigurations");
|
||||
} catch (ClassNotFoundException e) {
|
||||
Iris.info(C.RED + "Iris requires paper or above to function properly..");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("org.purpurmc.purpur.PurpurConfig");
|
||||
} catch (ClassNotFoundException e) {
|
||||
Iris.info("We recommend using Purpur for the best experience with Iris.");
|
||||
Iris.info("Purpur is a fork of Paper that is optimized for performance and stability.");
|
||||
Iris.info("Plugins that work on Spigot / Paper work on Purpur.");
|
||||
Iris.info("You can download it here: https://purpurmc.org");
|
||||
return false;
|
||||
}
|
||||
return passed;
|
||||
return IrisSafeguard.mode().tag(subTag);
|
||||
}
|
||||
|
||||
private void checkConfigHotload() {
|
||||
@@ -741,88 +690,11 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
}
|
||||
|
||||
public void splash() {
|
||||
if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String padd = Form.repeat(" ", 8);
|
||||
String padd2 = Form.repeat(" ", 4);
|
||||
String[] info = {"", "", "", "", "", padd2 + C.IRIS + " Iris", padd2 + C.GRAY + " by " + "<rainbow>Volmit Software", padd2 + C.GRAY + " v" + C.IRIS + getDescription().getVersion()};
|
||||
if (unstablemode) {
|
||||
info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()};
|
||||
}
|
||||
if (warningmode) {
|
||||
info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()};
|
||||
}
|
||||
|
||||
String[] splashstable = {
|
||||
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
|
||||
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.IRIS + " .(((()))). ",
|
||||
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.IRIS + " .((((((())))))). ",
|
||||
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.IRIS + " ((((((((())))))))) " + C.GRAY + " @",
|
||||
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.IRIS + " ((((((((-))))))))) " + C.GRAY + " @@",
|
||||
padd + C.GRAY + "@@@&&" + C.IRIS + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
|
||||
padd + C.GRAY + "@@" + C.IRIS + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
|
||||
padd + C.GRAY + "@" + C.IRIS + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + C.IRIS + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + C.IRIS + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
|
||||
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
|
||||
};
|
||||
|
||||
String[] splashunstable = {
|
||||
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
|
||||
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.RED + " .(((()))). ",
|
||||
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.RED + " .((((((())))))). ",
|
||||
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.RED + " ((((((((())))))))) " + C.GRAY + " @",
|
||||
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.RED + " ((((((((-))))))))) " + C.GRAY + " @@",
|
||||
padd + C.GRAY + "@@@&&" + C.RED + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
|
||||
padd + C.GRAY + "@@" + C.RED + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
|
||||
padd + C.GRAY + "@" + C.RED + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + C.RED + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + C.RED + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
|
||||
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
|
||||
};
|
||||
String[] splashwarning = {
|
||||
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
|
||||
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.GOLD + " .(((()))). ",
|
||||
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.GOLD + " .((((((())))))). ",
|
||||
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.GOLD + " ((((((((())))))))) " + C.GRAY + " @",
|
||||
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.GOLD + " ((((((((-))))))))) " + C.GRAY + " @@",
|
||||
padd + C.GRAY + "@@@&&" + C.GOLD + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
|
||||
padd + C.GRAY + "@@" + C.GOLD + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
|
||||
padd + C.GRAY + "@" + C.GOLD + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + C.GOLD + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + C.GOLD + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
|
||||
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@"
|
||||
};
|
||||
String[] splash;
|
||||
File freeSpace = new File(Bukkit.getWorldContainer() + ".");
|
||||
if (unstablemode) {
|
||||
splash = splashunstable;
|
||||
} else if (warningmode) {
|
||||
splash = splashwarning;
|
||||
} else {
|
||||
splash = splashstable;
|
||||
}
|
||||
|
||||
if (!passedserversoftware) {
|
||||
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
|
||||
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
|
||||
Iris.info("Java: " + getJava());
|
||||
if (getHardware.getProcessMemory() < 5999) {
|
||||
Iris.warn("6GB+ Ram is recommended");
|
||||
Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB");
|
||||
}
|
||||
Iris.info("Bukkit distro: " + Bukkit.getName());
|
||||
Iris.info("Server type & version: " + Bukkit.getName() + " v" + Bukkit.getVersion());
|
||||
Iris.info("Custom Biomes: " + INMS.get().countCustomBiomes());
|
||||
setupChecks();
|
||||
printPacks();
|
||||
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
splash[i] += info[i];
|
||||
}
|
||||
|
||||
Iris.info("\n\n " + new KList<>(splash).toString("\n") + "\n");
|
||||
IrisSafeguard.mode().trySplash();
|
||||
}
|
||||
|
||||
private void printPacks() {
|
||||
|
||||
@@ -170,7 +170,7 @@ public class IrisSettings {
|
||||
if (tectonicPlateSize > 0)
|
||||
return tectonicPlateSize;
|
||||
|
||||
return (int) (getHardware.getProcessMemory() / 200L);
|
||||
return (int) (getHardware.getProcessMemory() / 512L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.engine.object.IrisPosition;
|
||||
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.context.IrisContext;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
@@ -52,6 +54,7 @@ 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.plugin.VolmitSender;
|
||||
import com.volmit.iris.util.scheduling.jobs.Job;
|
||||
import lombok.SneakyThrows;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
@@ -92,6 +95,124 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
Iris.reportError(new Exception("This is a test"));
|
||||
}
|
||||
|
||||
@Decree(description = "Dev cmd to fix all the broken objects caused by faulty shrinkwarp")
|
||||
public void fixObjects(
|
||||
@Param(aliases = "dimension", description = "The dimension type to create the world with")
|
||||
IrisDimension type
|
||||
) {
|
||||
if (type == null) {
|
||||
sender().sendMessage("Type cant be null?");
|
||||
return;
|
||||
}
|
||||
|
||||
IrisData dm = IrisData.get(Iris.instance.getDataFolder("packs", type.getLoadKey()));
|
||||
var loader = dm.getObjectLoader();
|
||||
var processed = new KMap<String, IrisPosition>();
|
||||
|
||||
var objects = loader.getPossibleKeys();
|
||||
var pieces = dm.getJigsawPieceLoader().getPossibleKeys();
|
||||
var sender = sender();
|
||||
|
||||
sender.sendMessage(C.IRIS + "Found " + objects.length + " objects in " + type.getLoadKey());
|
||||
sender.sendMessage(C.IRIS + "Found " + pieces.length + " jigsaw pieces in " + type.getLoadKey());
|
||||
|
||||
final int total = objects.length;
|
||||
final AtomicInteger completed = new AtomicInteger();
|
||||
final AtomicInteger changed = new AtomicInteger();
|
||||
|
||||
new Job() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Fixing Objects";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
Arrays.stream(pieces).parallel()
|
||||
.map(dm.getJigsawPieceLoader()::load)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(piece -> {
|
||||
var offset = processed.compute(piece.getObject(), (key, o) -> {
|
||||
if (o != null) return o;
|
||||
var obj = loader.load(key);
|
||||
if (obj == null) return new IrisPosition();
|
||||
|
||||
obj.shrinkwrap();
|
||||
try {
|
||||
if (!obj.getShrinkOffset().isZero()) {
|
||||
changed.incrementAndGet();
|
||||
obj.write(obj.getLoadFile());
|
||||
}
|
||||
completeWork();
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to write object " + obj.getLoadKey());
|
||||
e.printStackTrace();
|
||||
return new IrisPosition();
|
||||
}
|
||||
|
||||
return new IrisPosition(obj.getShrinkOffset());
|
||||
});
|
||||
if (offset.getX() == 0 && offset.getY() == 0 && offset.getZ() == 0)
|
||||
return;
|
||||
|
||||
piece.getConnectors().forEach(connector -> connector.setPosition(connector.getPosition().add(offset)));
|
||||
|
||||
try {
|
||||
IO.writeAll(piece.getLoadFile(), dm.getGson().toJson(piece));
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to write jigsaw piece " + piece.getLoadKey());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
Arrays.stream(loader.getPossibleKeys()).parallel()
|
||||
.filter(key -> !processed.containsKey(key))
|
||||
.map(loader::load)
|
||||
.forEach(obj -> {
|
||||
if (obj == null) {
|
||||
completeWork();
|
||||
return;
|
||||
}
|
||||
|
||||
obj.shrinkwrap();
|
||||
if (obj.getShrinkOffset().isZero()) {
|
||||
completeWork();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
obj.write(obj.getLoadFile());
|
||||
completeWork();
|
||||
changed.incrementAndGet();
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to write object " + obj.getLoadKey());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeWork() {
|
||||
completed.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalWork() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWorkCompleted() {
|
||||
return completed.get();
|
||||
}
|
||||
}.execute(sender, () -> {
|
||||
var failed = total - completed.get();
|
||||
if (failed != 0) sender.sendMessage(C.IRIS + "" + failed + " objects failed!");
|
||||
if (changed.get() != 0) sender.sendMessage(C.IRIS + "" + changed.get() + " objects had their offsets changed!");
|
||||
else sender.sendMessage(C.IRIS + "No objects had their offsets changed!");
|
||||
});
|
||||
}
|
||||
|
||||
@Decree(description = "Test")
|
||||
public void mantle(@Param(defaultValue = "false") boolean plate, @Param(defaultValue = "21474836474") String name) throws Throwable {
|
||||
var base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
|
||||
|
||||
@@ -46,6 +46,16 @@ public class CommandEdit implements DecreeExecutor {
|
||||
sender().sendMessage(C.RED + "You must be in a studio world!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
sender().sendMessage(C.RED + "Cannot open files in headless environments!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Desktop.isDesktopSupported()) {
|
||||
sender().sendMessage(C.RED + "Desktop is not supported by this environment!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,22 +222,8 @@ public class CommandStudio implements DecreeExecutor {
|
||||
job.execute(sender(), latch::countDown);
|
||||
latch.await();
|
||||
|
||||
int sections = mantle.getWorldHeight() >> 4;
|
||||
chunkMap.forEach((pos, chunk) -> {
|
||||
var c = mantle.getChunk(pos.getX(), pos.getZ()).use();
|
||||
try {
|
||||
c.copyFlags(chunk);
|
||||
c.clear();
|
||||
for (int y = 0; y < sections; y++) {
|
||||
var slice = chunk.get(y);
|
||||
if (slice == null) continue;
|
||||
var s = c.getOrCreate(y);
|
||||
slice.getSliceMap().forEach(s::putSlice);
|
||||
}
|
||||
} finally {
|
||||
c.release();
|
||||
}
|
||||
});
|
||||
chunkMap.forEach((pos, chunk) ->
|
||||
mantle.getChunk(pos.getX(), pos.getZ()).copyFrom(chunk));
|
||||
} catch (Throwable e) {
|
||||
sender().sendMessage("Error while regenerating chunks");
|
||||
e.printStackTrace();
|
||||
@@ -702,8 +688,14 @@ public class CommandStudio implements DecreeExecutor {
|
||||
}
|
||||
|
||||
sender().sendMessage(C.GREEN + "Sending you to the studio world!");
|
||||
player().teleport(Iris.service(StudioSVC.class).getActiveProject().getActiveProvider().getTarget().getWorld().spawnLocation());
|
||||
player().setGameMode(GameMode.SPECTATOR);
|
||||
var player = player();
|
||||
PaperLib.teleportAsync(player(), Iris.service(StudioSVC.class)
|
||||
.getActiveProject()
|
||||
.getActiveProvider()
|
||||
.getTarget()
|
||||
.getWorld()
|
||||
.spawnLocation()
|
||||
).thenRun(() -> player.setGameMode(GameMode.SPECTATOR));
|
||||
}
|
||||
|
||||
@Decree(description = "Update your dimension projects VSCode workspace")
|
||||
|
||||
@@ -61,14 +61,14 @@ public class JigsawEditor implements Listener {
|
||||
private Location target;
|
||||
|
||||
public JigsawEditor(Player player, IrisJigsawPiece piece, IrisObject object, File saveLocation) {
|
||||
if (editors.containsKey(player)) {
|
||||
editors.get(player).close();
|
||||
}
|
||||
if (object == null) throw new RuntimeException("Object is null! " + piece.getObject());
|
||||
editors.compute(player, ($, current) -> {
|
||||
if (current != null) {
|
||||
current.exit();
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
editors.put(player, this);
|
||||
if (object == null) {
|
||||
throw new RuntimeException("Object is null! " + piece.getObject());
|
||||
}
|
||||
this.object = object;
|
||||
this.player = player;
|
||||
origin = player.getLocation().clone().add(0, 7, 0);
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.volmit.iris.core.link.ExternalDataProvider;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.data.IrisCustomData;
|
||||
import dev.lone.itemsadder.api.CustomBlock;
|
||||
import dev.lone.itemsadder.api.CustomStack;
|
||||
@@ -19,12 +18,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ItemAdderDataProvider extends ExternalDataProvider {
|
||||
|
||||
private final KSet<String> itemNamespaces = new KSet<>();
|
||||
private final KSet<String> blockNamespaces = new KSet<>();
|
||||
private volatile Set<String> itemNamespaces = Set.of();
|
||||
private volatile Set<String> blockNamespaces = Set.of();
|
||||
|
||||
public ItemAdderDataProvider() {
|
||||
super("ItemsAdder");
|
||||
@@ -94,9 +94,9 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
|
||||
|
||||
private void updateNamespaces(DataType dataType) {
|
||||
var namespaces = getTypes(dataType).stream().map(Identifier::namespace).collect(Collectors.toSet());
|
||||
var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces;
|
||||
currentNamespaces.removeIf(n -> !namespaces.contains(n));
|
||||
currentNamespaces.addAll(namespaces);
|
||||
if (dataType == DataType.ITEM) itemNamespaces = namespaces;
|
||||
else blockNamespaces = namespaces;
|
||||
Iris.debug("Updated ItemAdder namespaces: " + dataType + " - " + namespaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -103,7 +103,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
|
||||
}
|
||||
|
||||
public static void dereference() {
|
||||
dataLoaders.v().forEach(IrisData::cleanupEngine);
|
||||
dataLoaders.values().forEach(IrisData::cleanupEngine);
|
||||
}
|
||||
|
||||
public static int cacheSize() {
|
||||
|
||||
@@ -28,9 +28,10 @@ import java.util.List;
|
||||
public class INMS {
|
||||
private static final Version CURRENT = Boolean.getBoolean("iris.no-version-limit") ?
|
||||
new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, null) :
|
||||
new Version(21, 10, null);
|
||||
new Version(21, 11, null);
|
||||
|
||||
private static final List<Version> REVISION = List.of(
|
||||
new Version(21, 11, "v1_21_R7"),
|
||||
new Version(21, 9, "v1_21_R6"),
|
||||
new Version(21, 6, "v1_21_R5"),
|
||||
new Version(21, 5, "v1_21_R4"),
|
||||
|
||||
@@ -19,10 +19,8 @@
|
||||
package com.volmit.iris.core.nms;
|
||||
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||
import com.volmit.iris.core.nms.container.Pair;
|
||||
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
@@ -124,7 +122,7 @@ public interface INMSBinding {
|
||||
Color getBiomeColor(Location location, BiomeColor type);
|
||||
|
||||
default DataVersion getDataVersion() {
|
||||
return DataVersion.V1192;
|
||||
return DataVersion.V1_19_2;
|
||||
}
|
||||
|
||||
default int getSpawnChunkCount(World world) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.volmit.iris.core.nms.INMS;
|
||||
import com.volmit.iris.core.nms.datapack.v1192.DataFixerV1192;
|
||||
import com.volmit.iris.core.nms.datapack.v1206.DataFixerV1206;
|
||||
import com.volmit.iris.core.nms.datapack.v1213.DataFixerV1213;
|
||||
import com.volmit.iris.core.nms.datapack.v1217.DataFixerV1217;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
@@ -14,9 +15,10 @@ import java.util.function.Supplier;
|
||||
@Getter
|
||||
public enum DataVersion {
|
||||
UNSUPPORTED("0.0.0", 0, () -> null),
|
||||
V1192("1.19.2", 10, DataFixerV1192::new),
|
||||
V1205("1.20.6", 41, DataFixerV1206::new),
|
||||
V1213("1.21.3", 57, DataFixerV1213::new);
|
||||
V1_19_2("1.19.2", 10, DataFixerV1192::new),
|
||||
V1_20_5("1.20.6", 41, DataFixerV1206::new),
|
||||
V1_21_3("1.21.3", 57, DataFixerV1213::new),
|
||||
V1_21_11("1.21.11", 75, DataFixerV1217::new);
|
||||
private static final KMap<DataVersion, IDataFixer> cache = new KMap<>();
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final Supplier<IDataFixer> constructor;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.volmit.iris.core.nms.datapack.v1217;
|
||||
|
||||
import com.volmit.iris.core.nms.datapack.v1213.DataFixerV1213;
|
||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||
import com.volmit.iris.util.json.JSONArray;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class DataFixerV1217 extends DataFixerV1213 {
|
||||
private static final Map<Dimension, String> DIMENSIONS = Map.of(
|
||||
Dimension.OVERWORLD, """
|
||||
{
|
||||
"ambient_light": 0.0,
|
||||
"attributes": {
|
||||
"minecraft:audio/ambient_sounds": {
|
||||
"mood": {
|
||||
"block_search_extent": 8,
|
||||
"offset": 2.0,
|
||||
"sound": "minecraft:ambient.cave",
|
||||
"tick_delay": 6000
|
||||
}
|
||||
},
|
||||
"minecraft:audio/background_music": {
|
||||
"creative": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.creative"
|
||||
},
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.game"
|
||||
}
|
||||
},
|
||||
"minecraft:visual/cloud_color": "#ccffffff",
|
||||
"minecraft:visual/fog_color": "#c0d8ff",
|
||||
"minecraft:visual/sky_color": "#78a7ff"
|
||||
},
|
||||
"timelines": "#minecraft:in_overworld"
|
||||
}""",
|
||||
Dimension.NETHER, """
|
||||
{
|
||||
"ambient_light": 0.1,
|
||||
"attributes": {
|
||||
"minecraft:gameplay/sky_light_level": 4.0,
|
||||
"minecraft:gameplay/snow_golem_melts": true,
|
||||
"minecraft:visual/fog_end_distance": 96.0,
|
||||
"minecraft:visual/fog_start_distance": 10.0,
|
||||
"minecraft:visual/sky_light_color": "#7a7aff",
|
||||
"minecraft:visual/sky_light_factor": 0.0
|
||||
},
|
||||
"cardinal_light": "nether",
|
||||
"skybox": "none",
|
||||
"timelines": "#minecraft:in_nether"
|
||||
}""",
|
||||
Dimension.END, """
|
||||
{
|
||||
"ambient_light": 0.25,
|
||||
"attributes": {
|
||||
"minecraft:audio/ambient_sounds": {
|
||||
"mood": {
|
||||
"block_search_extent": 8,
|
||||
"offset": 2.0,
|
||||
"sound": "minecraft:ambient.cave",
|
||||
"tick_delay": 6000
|
||||
}
|
||||
},
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 6000,
|
||||
"replace_current_music": true,
|
||||
"sound": "minecraft:music.end"
|
||||
}
|
||||
},
|
||||
"minecraft:visual/fog_color": "#181318",
|
||||
"minecraft:visual/sky_color": "#000000",
|
||||
"minecraft:visual/sky_light_color": "#e580ff",
|
||||
"minecraft:visual/sky_light_factor": 0.0
|
||||
},
|
||||
"skybox": "end",
|
||||
"timelines": "#minecraft:in_end"
|
||||
}"""
|
||||
);
|
||||
|
||||
@Override
|
||||
public JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
|
||||
json = super.fixCustomBiome(biome, json);
|
||||
var effects = json.getJSONObject("effects");
|
||||
var attributes = new JSONObject();
|
||||
|
||||
attributes.put("minecraft:visual/fog_color", effects.remove("fog_color"));
|
||||
attributes.put("minecraft:visual/sky_color", effects.remove("sky_color"));
|
||||
attributes.put("minecraft:visual/water_fog_color", effects.remove("water_fog_color"));
|
||||
|
||||
JSONObject particle = (JSONObject) effects.remove("particle");
|
||||
if (particle != null) {
|
||||
attributes.put("minecraft:visual/ambient_particles", new JSONArray()
|
||||
.put(particle.getJSONObject("options")
|
||||
.put("probability", particle.get("probability"))));
|
||||
}
|
||||
json.put("attributes", attributes);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixDimension(Dimension dimension, JSONObject json) {
|
||||
super.fixDimension(dimension, json);
|
||||
|
||||
var attributes = new JSONObject();
|
||||
if ((Boolean) json.remove("ultrawarm")) {
|
||||
attributes.put("minecraft:gameplay/water_evaporates", true);
|
||||
attributes.put("minecraft:gameplay/fast_lava", true);
|
||||
attributes.put("minecraft:gameplay/snow_golem_melts", true);
|
||||
attributes.put("minecraft:visual/default_dripstone_particle", new JSONObject()
|
||||
.put("value", "minecraft:dripstone_drip_water_lava"));
|
||||
}
|
||||
|
||||
if ((Boolean) json.remove("bed_works")) {
|
||||
attributes.put("minecraft:gameplay/bed_rule", new JSONObject()
|
||||
.put("can_set_spawn", "always")
|
||||
.put("can_sleep", "when_dark")
|
||||
.put("error_message", new JSONObject()
|
||||
.put("translate", "block.minecraft.bed.no_sleep")));
|
||||
} else {
|
||||
attributes.put("minecraft:gameplay/bed_rule", new JSONObject()
|
||||
.put("can_set_spawn", "never")
|
||||
.put("can_sleep", "never")
|
||||
.put("explodes", true));
|
||||
}
|
||||
|
||||
attributes.put("minecraft:gameplay/respawn_anchor_works", json.remove("respawn_anchor_works"));
|
||||
attributes.put("minecraft:gameplay/piglins_zombify", json.remove("piglin_safe"));
|
||||
attributes.put("minecraft:gameplay/can_start_raid", json.remove("has_raids"));
|
||||
|
||||
var cloud_height = json.remove("cloud_height");
|
||||
if (cloud_height != null) attributes.put("minecraft:visual/cloud_height", cloud_height);
|
||||
|
||||
boolean natural = (Boolean) json.remove("natural");
|
||||
attributes.put("minecraft:gameplay/nether_portal_spawns_piglin", natural);
|
||||
if (natural != (dimension == Dimension.OVERWORLD)) {
|
||||
attributes.put("minecraft:gameplay/eyeblossom_open", natural);
|
||||
attributes.put("minecraft:gameplay/creaking_active", natural);
|
||||
}
|
||||
|
||||
//json.put("has_fixed_time", json.remove("fixed_time") != null); //TODO investigate
|
||||
json.put("attributes", attributes);
|
||||
|
||||
json.remove("effects");
|
||||
var defaults = new JSONObject(DIMENSIONS.get(dimension));
|
||||
merge(json, defaults);
|
||||
}
|
||||
|
||||
private void merge(JSONObject base, JSONObject override) {
|
||||
for (String key : override.keySet()) {
|
||||
switch (base.opt(key)) {
|
||||
case null -> base.put(key, override.opt(key));
|
||||
case JSONObject base1 when override.opt(key) instanceof JSONObject override1 -> merge(base1, override1);
|
||||
case JSONArray base1 when override.opt(key) instanceof JSONArray override1 -> {
|
||||
for (Object o : override1) {
|
||||
base1.put(o);
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,7 +225,7 @@ public class IrisProject {
|
||||
sender.sendMessage("Can't find dimension: " + getName());
|
||||
return;
|
||||
} else if (sender.isPlayer()) {
|
||||
sender.player().setGameMode(GameMode.SPECTATOR);
|
||||
J.s(() -> sender.player().setGameMode(GameMode.SPECTATOR));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.volmit.iris.core.safeguard;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import io.papermc.lib.PaperLib;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class IrisSafeguard {
|
||||
private static final AtomicBoolean sfg = new AtomicBoolean(false);
|
||||
public static boolean unstablemode = false;
|
||||
public static boolean warningmode = false;
|
||||
public static boolean stablemode = false;
|
||||
|
||||
public static void IrisSafeguardSystem() {
|
||||
Iris.info("Enabled Iris SafeGuard");
|
||||
ServerBootSFG.BootCheck();
|
||||
}
|
||||
|
||||
public static void splash(boolean early) {
|
||||
if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode))
|
||||
return;
|
||||
|
||||
if (!sfg.getAndSet(true)) {
|
||||
Iris.instance.splash();
|
||||
UtilsSFG.splash();
|
||||
}
|
||||
}
|
||||
|
||||
public static String mode() {
|
||||
if (unstablemode) {
|
||||
return "unstable";
|
||||
} else if (warningmode) {
|
||||
return "warning";
|
||||
} else {
|
||||
return "stable";
|
||||
}
|
||||
}
|
||||
|
||||
public static void suggestPaper() {
|
||||
PaperLib.suggestPaper(Iris.instance);
|
||||
}
|
||||
|
||||
public static KMap<String, Object> asContext() {
|
||||
KMap<String, Object> m = new KMap<>();
|
||||
m.put("diskSpace", !ServerBootSFG.hasEnoughDiskSpace);
|
||||
m.put("javaVersion", !ServerBootSFG.isCorrectJDK);
|
||||
m.put("jre", ServerBootSFG.isJRE);
|
||||
m.put("missingAgent", ServerBootSFG.missingAgent);
|
||||
m.put("missingDimensionTypes", ServerBootSFG.missingDimensionTypes);
|
||||
m.put("failedInjection", ServerBootSFG.failedInjection);
|
||||
m.put("unsupportedVersion", ServerBootSFG.unsuportedversion);
|
||||
m.put("serverSoftware", !ServerBootSFG.passedserversoftware);
|
||||
KList<String> incompatiblePlugins = new KList<>();
|
||||
ServerBootSFG.incompatibilities.forEach((plugin, present) -> {
|
||||
if (present) incompatiblePlugins.add(plugin);
|
||||
});
|
||||
m.put("plugins", incompatiblePlugins);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
package com.volmit.iris.core.safeguard;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.util.format.C;
|
||||
|
||||
public class ModesSFG {
|
||||
public static void selectMode() {
|
||||
if (IrisSafeguard.unstablemode) {
|
||||
Iris.safeguard(C.DARK_RED + "Iris is running in Unstable Mode");
|
||||
unstable();
|
||||
}
|
||||
if (IrisSafeguard.warningmode) {
|
||||
Iris.safeguard(C.GOLD + "Iris is running in Warning Mode");
|
||||
warning();
|
||||
}
|
||||
if (IrisSafeguard.stablemode) {
|
||||
stable();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stable() {
|
||||
Iris.safeguard(C.BLUE + "Iris is running Stable");
|
||||
}
|
||||
|
||||
public static void unstable() {
|
||||
|
||||
UtilsSFG.printIncompatibleWarnings();
|
||||
|
||||
if (IrisSafeguard.unstablemode) {
|
||||
Iris.info("");
|
||||
Iris.info(C.DARK_GRAY + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--");
|
||||
Iris.info(C.RED + "Iris is running in unstable mode which may cause the following issues:");
|
||||
Iris.info(C.DARK_RED + "Server Issues");
|
||||
Iris.info(C.RED + "- Server won't boot");
|
||||
Iris.info(C.RED + "- Data Loss");
|
||||
Iris.info(C.RED + "- Unexpected behavior.");
|
||||
Iris.info(C.RED + "- And More...");
|
||||
Iris.info(C.DARK_RED + "World Issues");
|
||||
Iris.info(C.RED + "- Worlds can't load due to corruption.");
|
||||
Iris.info(C.RED + "- Worlds may slowly corrupt until they can't load.");
|
||||
Iris.info(C.RED + "- World data loss.");
|
||||
Iris.info(C.RED + "- And More...");
|
||||
Iris.info(C.DARK_RED + "ATTENTION: " + C.RED + "While running Iris in unstable mode, you won't be eligible for support.");
|
||||
Iris.info(C.DARK_RED + "CAUSE: " + C.RED + UtilsSFG.MSGIncompatibleWarnings());
|
||||
|
||||
if (IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode) {
|
||||
Iris.info(C.DARK_RED + "Boot Unstable is set to true, continuing with the startup process.");
|
||||
} else {
|
||||
Iris.info(C.DARK_RED + "Go to plugins/iris/settings.json and set DoomsdayAnnihilationSelfDestructMode to true if you wish to proceed.");
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(Long.MAX_VALUE);
|
||||
} catch (InterruptedException e) {
|
||||
// no
|
||||
}
|
||||
}
|
||||
}
|
||||
Iris.info("");
|
||||
}
|
||||
}
|
||||
|
||||
public static void warning() {
|
||||
|
||||
UtilsSFG.printIncompatibleWarnings();
|
||||
|
||||
if (IrisSafeguard.warningmode) {
|
||||
Iris.info("");
|
||||
Iris.info(C.DARK_GRAY + "--==<" + C.GOLD + " IMPORTANT " + C.DARK_GRAY + ">==--");
|
||||
Iris.info(C.GOLD + "Iris is running in warning mode which may cause the following issues:");
|
||||
Iris.info(C.YELLOW + "- Data Loss");
|
||||
Iris.info(C.YELLOW + "- Errors");
|
||||
Iris.info(C.YELLOW + "- Broken worlds");
|
||||
Iris.info(C.YELLOW + "- Unexpected behavior.");
|
||||
Iris.info(C.YELLOW + "- And perhaps further complications.");
|
||||
Iris.info(C.GOLD + "CAUSE: " + C.YELLOW + UtilsSFG.MSGIncompatibleWarnings());
|
||||
Iris.info("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.volmit.iris.core.safeguard;
|
||||
|
||||
public class PerformanceSFG {
|
||||
public static void calculatePerformance() {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
package com.volmit.iris.core.safeguard;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisWorlds;
|
||||
import com.volmit.iris.core.nms.INMS;
|
||||
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.util.agent.Agent;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.io.File;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.volmit.iris.Iris.getJavaVersion;
|
||||
import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
|
||||
|
||||
public class ServerBootSFG {
|
||||
public static final Map<String, Boolean> incompatibilities = new HashMap<>();
|
||||
public static boolean isCorrectJDK = true;
|
||||
public static boolean hasEnoughDiskSpace = true;
|
||||
public static boolean isJRE = false;
|
||||
public static boolean hasPrivileges = true;
|
||||
public static boolean unsuportedversion = false;
|
||||
public static boolean missingDimensionTypes = false;
|
||||
public static boolean missingAgent = false;
|
||||
public static boolean failedInjection = false;
|
||||
protected static boolean safeguardPassed;
|
||||
public static boolean passedserversoftware = true;
|
||||
protected static int count;
|
||||
protected static byte severityLow;
|
||||
protected static byte severityMedium;
|
||||
protected static byte severityHigh;
|
||||
public static String allIncompatibilities;
|
||||
|
||||
public static void BootCheck() {
|
||||
Iris.info("Checking for possible conflicts..");
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
Plugin[] plugins = pluginManager.getPlugins();
|
||||
|
||||
incompatibilities.clear();
|
||||
incompatibilities.put("dynmap", false);
|
||||
incompatibilities.put("Stratos", false);
|
||||
|
||||
String pluginName;
|
||||
for (Plugin plugin : plugins) {
|
||||
pluginName = plugin.getName();
|
||||
Boolean flag = incompatibilities.get(pluginName);
|
||||
if (flag != null && !flag) {
|
||||
severityHigh++;
|
||||
incompatibilities.put(pluginName, true);
|
||||
}
|
||||
}
|
||||
|
||||
StringJoiner joiner = new StringJoiner(", ");
|
||||
for (Map.Entry<String, Boolean> entry : incompatibilities.entrySet()) {
|
||||
if (entry.getValue()) {
|
||||
joiner.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
// Legacy ServerInfo
|
||||
String distro = Bukkit.getName().toLowerCase();
|
||||
if (
|
||||
!distro.contains("purpur") &&
|
||||
!distro.contains("paper") &&
|
||||
!distro.contains("spigot") &&
|
||||
!distro.contains("pufferfish") &&
|
||||
!distro.contains("bukkit")) {
|
||||
|
||||
|
||||
passedserversoftware = false;
|
||||
joiner.add("Server Software");
|
||||
severityMedium++;
|
||||
}
|
||||
|
||||
|
||||
if (INMS.get() instanceof NMSBinding1X) {
|
||||
unsuportedversion = true;
|
||||
joiner.add("Unsupported Minecraft Version");
|
||||
severityHigh++;
|
||||
}
|
||||
|
||||
if (!List.of(21).contains(getJavaVersion())) {
|
||||
isCorrectJDK = false;
|
||||
joiner.add("Unsupported Java version");
|
||||
severityMedium++;
|
||||
}
|
||||
|
||||
if (!isJDK()) {
|
||||
isJRE = true;
|
||||
joiner.add("Unsupported JDK");
|
||||
severityMedium++;
|
||||
}
|
||||
|
||||
// if (!hasPrivileges()){
|
||||
// hasPrivileges = false;
|
||||
// joiner.add("Insufficient Privileges");
|
||||
// severityMedium++;
|
||||
// } Some servers dont like this
|
||||
|
||||
if (!enoughDiskSpace()){
|
||||
hasEnoughDiskSpace = false;
|
||||
joiner.add("Insufficient Disk Space");
|
||||
severityMedium++;
|
||||
}
|
||||
|
||||
if (!Agent.install()) {
|
||||
missingAgent = true;
|
||||
joiner.add("Missing Java Agent");
|
||||
severityHigh++;
|
||||
} else {
|
||||
if (missingDimensionTypes()) {
|
||||
missingDimensionTypes = true;
|
||||
joiner.add("Missing Dimension Types");
|
||||
severityHigh++;
|
||||
}
|
||||
if (!INMS.get().injectBukkit()) {
|
||||
failedInjection = true;
|
||||
joiner.add("Failed Bukkit Injection");
|
||||
severityHigh++;
|
||||
}
|
||||
}
|
||||
|
||||
allIncompatibilities = joiner.toString();
|
||||
|
||||
safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0);
|
||||
count = severityHigh + severityMedium + severityLow;
|
||||
if (safeguardPassed) {
|
||||
stablemode = true;
|
||||
Iris.safeguard("Stable mode has been activated.");
|
||||
}
|
||||
if (!safeguardPassed) {
|
||||
if (severityMedium >= 1 && severityHigh == 0) {
|
||||
warningmode = true;
|
||||
Iris.safeguard("Warning mode has been activated.");
|
||||
}
|
||||
if (severityHigh >= 1) {
|
||||
unstablemode = true;
|
||||
Iris.safeguard("Unstable mode has been activated.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isJDK() {
|
||||
try {
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
// If the compiler is null, it means this is a JRE environment, not a JDK.
|
||||
return compiler != null;
|
||||
} catch (Exception ignored) {}
|
||||
return false;
|
||||
}
|
||||
public static boolean hasPrivileges() {
|
||||
Path pv = Paths.get(Bukkit.getWorldContainer() + "iristest.json");
|
||||
try (FileChannel fc = FileChannel.open(pv, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
||||
if (Files.isReadable(pv) && Files.isWritable(pv)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean enoughDiskSpace() {
|
||||
File freeSpace = Bukkit.getWorldContainer();
|
||||
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
|
||||
return gigabytes > 3;
|
||||
}
|
||||
|
||||
private static boolean checkJavac(String path) {
|
||||
return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists());
|
||||
}
|
||||
|
||||
private static boolean missingDimensionTypes() {
|
||||
return INMS.get().missingDimensionTypes(getDimensionTypes().toArray(String[]::new));
|
||||
}
|
||||
|
||||
private static KSet<String> getDimensionTypes() {
|
||||
return IrisWorlds.get()
|
||||
.getDimensions()
|
||||
.map(IrisDimension::getDimensionTypeKey)
|
||||
.collect(Collectors.toCollection(KSet::new));
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.volmit.iris.core.safeguard;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.agent.Agent;
|
||||
import com.volmit.iris.util.format.C;
|
||||
|
||||
public class UtilsSFG {
|
||||
public static void splash() {
|
||||
ModesSFG.selectMode();
|
||||
}
|
||||
|
||||
public static void printIncompatibleWarnings() {
|
||||
String[] parts = Iris.instance.getDescription().getVersion().split("-");
|
||||
String minVersion = parts[1];
|
||||
String maxVersion = parts[2];
|
||||
|
||||
if (ServerBootSFG.safeguardPassed) {
|
||||
Iris.safeguard(C.BLUE + "0 Conflicts found");
|
||||
} else {
|
||||
if (IrisSafeguard.unstablemode) {
|
||||
Iris.safeguard(C.DARK_RED + "" + ServerBootSFG.count + " Conflicts found");
|
||||
}
|
||||
if (IrisSafeguard.warningmode) {
|
||||
Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found");
|
||||
}
|
||||
|
||||
if (ServerBootSFG.incompatibilities.get("dynmap")) {
|
||||
Iris.safeguard(C.RED + "Dynmap");
|
||||
Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server.");
|
||||
Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap.");
|
||||
}
|
||||
if (ServerBootSFG.incompatibilities.get("Stratos")) {
|
||||
Iris.safeguard(C.YELLOW + "Stratos");
|
||||
Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins.");
|
||||
}
|
||||
if (ServerBootSFG.unsuportedversion) {
|
||||
Iris.safeguard(C.RED + "Server Version");
|
||||
Iris.safeguard(C.RED + "- Iris only supports " + minVersion + " > " + maxVersion);
|
||||
}
|
||||
if (ServerBootSFG.missingDimensionTypes) {
|
||||
Iris.safeguard(C.RED + "Dimension Types");
|
||||
Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded.");
|
||||
Iris.safeguard(C.RED + "- If this still happens after a restart please contact support.");
|
||||
}
|
||||
if (ServerBootSFG.missingAgent) {
|
||||
Iris.safeguard(C.RED + "Java Agent");
|
||||
Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments.");
|
||||
Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath());
|
||||
}
|
||||
if (!ServerBootSFG.passedserversoftware) {
|
||||
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
||||
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");
|
||||
}
|
||||
if (!ServerBootSFG.hasPrivileges) {
|
||||
Iris.safeguard(C.YELLOW + "Insufficient Privileges");
|
||||
Iris.safeguard(C.YELLOW + "- The server has insufficient Privileges to run iris. Please contact support.");
|
||||
}
|
||||
if (!ServerBootSFG.hasEnoughDiskSpace) {
|
||||
Iris.safeguard(C.YELLOW + "Insufficient Disk Space");
|
||||
Iris.safeguard(C.YELLOW + "- The server has insufficient Free DiskSpace to run iris required 3GB+.");
|
||||
}
|
||||
if (!ServerBootSFG.isCorrectJDK) {
|
||||
Iris.safeguard(C.YELLOW + "Unsupported java version");
|
||||
Iris.safeguard(C.YELLOW + "- Please consider using JDK 21 Instead of JDK " + Iris.getJavaVersion());
|
||||
}
|
||||
if (ServerBootSFG.isJRE) {
|
||||
Iris.safeguard(C.YELLOW + "Unsupported Server JDK");
|
||||
Iris.safeguard(C.YELLOW + "- Please consider using JDK 21 Instead of JRE " + Iris.getJavaVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String MSGIncompatibleWarnings() {
|
||||
return ServerBootSFG.allIncompatibilities;
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,21 @@ package com.volmit.iris.core.tools;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.object.*;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
||||
import com.volmit.iris.util.nbt.io.NamedTag;
|
||||
import com.volmit.iris.util.nbt.tag.*;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import com.volmit.iris.util.reflect.V;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.FileUtil;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -39,13 +31,17 @@ public class IrisConverter {
|
||||
sender.sendMessage("No schematic files to convert found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
var stopwatch = PrecisionStopwatch.start();
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(1);
|
||||
executorService.submit(() -> {
|
||||
for (File schem : fileList) {
|
||||
try {
|
||||
for (File schem : fileList) {
|
||||
try {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
IrisObject object;
|
||||
boolean largeObject = false;
|
||||
NamedTag tag = null;
|
||||
NamedTag tag;
|
||||
try {
|
||||
tag = NBTUtil.read(schem);
|
||||
} catch (IOException e) {
|
||||
@@ -53,17 +49,20 @@ public class IrisConverter {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
CompoundTag compound = (CompoundTag) tag.getTag();
|
||||
int version = resolveVersion(compound);
|
||||
if (!(version == 2 || version == 3))
|
||||
throw new RuntimeException(C.RED + "Unsupported schematic version: " + version);
|
||||
|
||||
if (compound.containsKey("Palette") && compound.containsKey("Width") && compound.containsKey("Height") && compound.containsKey("Length")) {
|
||||
compound = version == 3 ? (CompoundTag) compound.get("Schematic") : compound;
|
||||
int objW = ((ShortTag) compound.get("Width")).getValue();
|
||||
int objH = ((ShortTag) compound.get("Height")).getValue();
|
||||
int objD = ((ShortTag) compound.get("Length")).getValue();
|
||||
int i = -1;
|
||||
int mv = objW * objH * objD;
|
||||
AtomicInteger v = new AtomicInteger(0);
|
||||
if (mv > 500_000) {
|
||||
if (mv > 2_000_000) {
|
||||
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");
|
||||
if (sender.isPlayer()) {
|
||||
i = J.ar(() -> {
|
||||
@@ -72,6 +71,7 @@ public class IrisConverter {
|
||||
}
|
||||
}
|
||||
|
||||
compound = version == 3 ? (CompoundTag) compound.get("Blocks") : compound;
|
||||
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()) {
|
||||
@@ -82,14 +82,16 @@ public class IrisConverter {
|
||||
blockmap.put(blockId, bd);
|
||||
}
|
||||
|
||||
ByteArrayTag byteArray = (ByteArrayTag) compound.get("BlockData");
|
||||
boolean isBytes = version == 3 ? compound.getByteArrayTag("Data").length() < 128 : ((IntTag) compound.get("PaletteMax")).getValue() < 128;
|
||||
ByteArrayTag byteArray = version == 3 ? (ByteArrayTag) compound.get("Data") : (ByteArrayTag) compound.get("BlockData");
|
||||
byte[] originalBlockArray = byteArray.getValue();
|
||||
|
||||
IrisObject object = new IrisObject(objW, objH, objD);
|
||||
var din = new DataInputStream(new ByteArrayInputStream(originalBlockArray));
|
||||
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(Byte.toUnsignedInt(originalBlockArray[v.get()]));
|
||||
int blockIndex = isBytes ? din.read() & 0xFF : Varint.readUnsignedVarInt(din);
|
||||
BlockData bd = blockmap.get(blockIndex);
|
||||
if (!bd.getMaterial().isAir()) {
|
||||
object.setUnsigned(w, h, d, bd);
|
||||
}
|
||||
@@ -97,42 +99,59 @@ public class IrisConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i != -1) J.car(i);
|
||||
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 (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"));
|
||||
counter.incrementAndGet();
|
||||
if (sender.isPlayer()) {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
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"));
|
||||
}
|
||||
FileUtils.delete(schem);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(C.RED + "Failed to save: " + schem.getName());
|
||||
throw new IOException(e);
|
||||
}
|
||||
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"));
|
||||
}
|
||||
FileUtils.delete(schem);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Iris.info(C.RED + "Failed to convert: " + schem.getName());
|
||||
if (sender.isPlayer()) {
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(C.RED + "Failed to convert: " + schem.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
sender.sendMessage(C.GRAY + "converted: " + fileList.length);
|
||||
stopwatch.end();
|
||||
if (counter.get() != 0) {
|
||||
sender.sendMessage(C.GRAY + "Converted: " + counter.get() + " in " + Form.duration(stopwatch.getMillis()));
|
||||
}
|
||||
if (counter.get() < fileList.length) {
|
||||
sender.sendMessage(C.RED + "Some schematics failed to convert. Check the console for details.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static int resolveVersion(CompoundTag compound) throws Exception {
|
||||
try {
|
||||
|
||||
IntTag root = compound.getIntTag("Version");
|
||||
if (root != null) {
|
||||
return root.getValue();
|
||||
}
|
||||
CompoundTag schematic = (CompoundTag) compound.get("Schematic");
|
||||
return schematic.getIntTag("Version").getValue();
|
||||
} catch (NullPointerException e) {
|
||||
throw new Exception("Cannot resolve schematic version", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.service.ExternalDataSVC;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedWorldManager;
|
||||
import com.volmit.iris.engine.object.*;
|
||||
@@ -57,13 +58,10 @@ import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -81,6 +79,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
private final ChronoLatch cln;
|
||||
private final ChronoLatch chunkUpdater;
|
||||
private final ChronoLatch chunkDiscovery;
|
||||
private final KMap<Long, Future<?>> cleanup = new KMap<>();
|
||||
private final ScheduledExecutorService cleanupService;
|
||||
private double energy = 25;
|
||||
private int entityCount = 0;
|
||||
private long charge = 0;
|
||||
@@ -98,6 +98,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
looper = null;
|
||||
chunkUpdater = null;
|
||||
chunkDiscovery = null;
|
||||
cleanupService = null;
|
||||
id = -1;
|
||||
}
|
||||
|
||||
@@ -109,6 +110,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
cl = new ChronoLatch(3000);
|
||||
ecl = new ChronoLatch(250);
|
||||
clw = new ChronoLatch(1000, true);
|
||||
cleanupService = Executors.newSingleThreadScheduledExecutor(runnable -> {
|
||||
var thread = new Thread(runnable, "Iris Mantle Cleanup " + getTarget().getWorld().name());
|
||||
thread.setPriority(Thread.MIN_PRIORITY);
|
||||
return thread;
|
||||
});
|
||||
id = engine.getCacheID();
|
||||
energy = 25;
|
||||
looper = new Looper() {
|
||||
@@ -122,10 +128,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
getEngine().getWorld().tryGetRealWorld();
|
||||
}
|
||||
|
||||
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem() && !IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()) {
|
||||
return 3000;
|
||||
}
|
||||
|
||||
if (getEngine().getWorld().hasRealWorld()) {
|
||||
if (getEngine().getWorld().getPlayers().isEmpty()) {
|
||||
return 5000;
|
||||
@@ -139,6 +141,13 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
discoverChunks();
|
||||
}
|
||||
|
||||
if (cln.flip()) {
|
||||
engine.getEngineData().cleanup(getEngine());
|
||||
}
|
||||
|
||||
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem() && !IrisSettings.get().getWorld().isAnbientEntitySpawningSystem()) {
|
||||
return 3000;
|
||||
}
|
||||
|
||||
if (getDimension().isInfiniteEnergy()) {
|
||||
energy += 1000;
|
||||
@@ -150,10 +159,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
fixEnergy();
|
||||
}
|
||||
|
||||
if (cln.flip()) {
|
||||
engine.getEngineData().cleanup(getEngine());
|
||||
}
|
||||
|
||||
if (precount != null) {
|
||||
entityCount = 0;
|
||||
for (Entity i : precount) {
|
||||
@@ -181,7 +186,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
};
|
||||
looper.setPriority(Thread.MIN_PRIORITY);
|
||||
looper.setName("Iris World Manager");
|
||||
looper.setName("Iris World Manager " + getTarget().getWorld().name());
|
||||
looper.start();
|
||||
}
|
||||
|
||||
@@ -425,16 +430,14 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
var ref = new WeakReference<>(e.getWorld());
|
||||
int cX = e.getX(), cZ = e.getZ();
|
||||
J.s(() -> {
|
||||
World world = ref.get();
|
||||
if (world == null || !world.isChunkLoaded(cX, cZ))
|
||||
return;
|
||||
Long key = Cache.key(e);
|
||||
cleanup.put(key, cleanupService.schedule(() -> {
|
||||
cleanup.remove(key);
|
||||
energy += 0.3;
|
||||
fixEnergy();
|
||||
getEngine().cleanupMantleChunk(cX, cZ);
|
||||
}, IrisSettings.get().getPerformance().mantleCleanupDelay);
|
||||
}, Math.max(IrisSettings.get().getPerformance().mantleCleanupDelay * 50L, 0), TimeUnit.MILLISECONDS));
|
||||
|
||||
if (generated) {
|
||||
//INMS.get().injectBiomesFromMantle(e, getMantle());
|
||||
@@ -458,6 +461,14 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkUnload(Chunk e) {
|
||||
final var future = cleanup.remove(Cache.key(e));
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void spawn(IrisPosition block, IrisSpawner spawner, boolean initial) {
|
||||
if (getEngine().isClosed()) {
|
||||
return;
|
||||
|
||||
@@ -851,7 +851,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
}
|
||||
}
|
||||
|
||||
IrisBiome biome = getBiome(x, y, z);
|
||||
IrisBiome biome = getSurfaceBiome(x, z);
|
||||
|
||||
for (IrisObjectPlacement i : biome.getObjects()) {
|
||||
if (i.getPlace().contains(object)) {
|
||||
@@ -1000,7 +1000,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
|
||||
default void cleanupMantleChunk(int x, int z) {
|
||||
if (IrisSettings.get().getPerformance().isTrimMantleInStudio() || !isStudio()) {
|
||||
J.a(() -> getMantle().cleanupChunk(x, z));
|
||||
getMantle().cleanupChunk(x, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.event.world.WorldSaveEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
@@ -125,6 +126,13 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(ChunkUnloadEvent e) {
|
||||
if (e.getChunk().getWorld().equals(getTarget().getWorld().realWorld())) {
|
||||
onChunkUnload(e.getChunk());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
|
||||
@@ -45,6 +45,8 @@ public interface EngineWorldManager {
|
||||
|
||||
void onChunkLoad(Chunk e, boolean generated);
|
||||
|
||||
void onChunkUnload(Chunk e);
|
||||
|
||||
void chargeEnergy();
|
||||
|
||||
void teleportAsync(PlayerTeleportEvent e);
|
||||
|
||||
@@ -69,7 +69,7 @@ public class PlannedPiece {
|
||||
this.setRotation(rot);
|
||||
this.ogObject = data.getObjectLoader().load(piece.getObject());
|
||||
this.object = structure.rotated(piece, rotation);
|
||||
this.piece = rotation.rotateCopy(piece);
|
||||
this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset()));
|
||||
this.piece.setLoadKey(piece.getLoadKey());
|
||||
this.object.setLoadKey(piece.getObject());
|
||||
this.ogObject.setLoadKey(piece.getObject());
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.volmit.iris.util.matter.Matter;
|
||||
import com.volmit.iris.util.matter.MatterCavern;
|
||||
import com.volmit.iris.util.matter.TileWrapper;
|
||||
import com.volmit.iris.util.noise.CNG;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import lombok.Data;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.Vector;
|
||||
@@ -61,17 +62,22 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
this.engineMantle = engineMantle;
|
||||
this.mantle = mantle;
|
||||
this.radius = radius * 2;
|
||||
int d = this.radius + 1;
|
||||
this.cachedChunks = multicore ? new KMap<>(d * d, 0.75f, Math.max(32, Runtime.getRuntime().availableProcessors() * 4)) : new HashMap<>(d * d);
|
||||
final int d = this.radius + 1;
|
||||
this.cachedChunks = multicore ? new KMap<>(d * d, 0.75f, Math.max(32, Runtime.getRuntime().availableProcessors() * 4)) : new Long2ObjectOpenHashMap<>(d * d);
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
|
||||
int r = radius / 2;
|
||||
for (int i = -r; i <= r; i++) {
|
||||
for (int j = -r; j <= r; j++) {
|
||||
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
|
||||
}
|
||||
}
|
||||
final int parallelism = multicore ? Runtime.getRuntime().availableProcessors() / 2 : 4;
|
||||
final var map = multicore ? cachedChunks : new KMap<Long, MantleChunk>(d * d, 1f, parallelism);
|
||||
mantle.getChunks(
|
||||
x - radius,
|
||||
x + radius,
|
||||
z - radius,
|
||||
z + radius,
|
||||
parallelism,
|
||||
(i, j, c) -> map.put(Cache.key(i, j), c.use())
|
||||
);
|
||||
if (!multicore) cachedChunks.putAll(map);
|
||||
}
|
||||
|
||||
private static Set<IrisPosition> getBallooned(Set<IrisPosition> vset, double radius) {
|
||||
|
||||
@@ -98,6 +98,8 @@ public class IrisObject extends IrisRegistrant {
|
||||
@Getter
|
||||
@Setter
|
||||
private transient Vector3i center;
|
||||
@Getter
|
||||
private transient Vector3i shrinkOffset;
|
||||
|
||||
public IrisObject(int w, int h, int d) {
|
||||
blocks = new VectorMap<>();
|
||||
@@ -106,6 +108,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
this.h = h;
|
||||
this.d = d;
|
||||
center = new Vector3i(w / 2, h / 2, d / 2);
|
||||
shrinkOffset = new Vector3i(0, 0, 0);
|
||||
var lock = new ReentrantReadWriteLock();
|
||||
readLock = lock.readLock();
|
||||
writeLock = lock.writeLock();
|
||||
@@ -305,6 +308,9 @@ public class IrisObject extends IrisRegistrant {
|
||||
blocks.put(new Vector3i(din.readShort(), din.readShort(), din.readShort()), B.get(din.readUTF()));
|
||||
}
|
||||
|
||||
if (din.available() == 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
int size = din.readInt();
|
||||
|
||||
@@ -313,7 +319,6 @@ public class IrisObject extends IrisRegistrant {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +328,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
this.h = din.readInt();
|
||||
this.d = din.readInt();
|
||||
if (!din.readUTF().equals("Iris V2 IOB;")) {
|
||||
return;
|
||||
throw new HeaderException();
|
||||
}
|
||||
center = new Vector3i(w / 2, h / 2, d / 2);
|
||||
int s = din.readShort();
|
||||
@@ -470,16 +475,14 @@ public class IrisObject extends IrisRegistrant {
|
||||
}
|
||||
|
||||
public void read(File file) throws IOException {
|
||||
var fin = new BufferedInputStream(new FileInputStream(file));
|
||||
try {
|
||||
try (var fin = new BufferedInputStream(new FileInputStream(file))) {
|
||||
read(fin);
|
||||
fin.close();
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
fin.close();
|
||||
fin = new BufferedInputStream(new FileInputStream(file));
|
||||
readLegacy(fin);
|
||||
fin.close();
|
||||
if (!(e instanceof HeaderException))
|
||||
Iris.reportError(e);
|
||||
try (var fin = new BufferedInputStream(new FileInputStream(file))) {
|
||||
readLegacy(fin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,8 +507,9 @@ public class IrisObject extends IrisRegistrant {
|
||||
}
|
||||
|
||||
public void shrinkwrap() {
|
||||
BlockVector min = new BlockVector();
|
||||
BlockVector max = new BlockVector();
|
||||
if (blocks.isEmpty()) return;
|
||||
BlockVector min = new BlockVector(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
BlockVector max = new BlockVector(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
|
||||
|
||||
for (BlockVector i : blocks.keys()) {
|
||||
min.setX(Math.min(min.getX(), i.getX()));
|
||||
@@ -520,6 +524,31 @@ public class IrisObject extends IrisRegistrant {
|
||||
h = max.getBlockY() - min.getBlockY() + 1;
|
||||
d = max.getBlockZ() - min.getBlockZ() + 1;
|
||||
center = new Vector3i(w / 2, h / 2, d / 2);
|
||||
|
||||
Vector3i offset = new Vector3i(
|
||||
-center.getBlockX() - min.getBlockX(),
|
||||
-center.getBlockY() - min.getBlockY(),
|
||||
-center.getBlockZ() - min.getBlockZ()
|
||||
);
|
||||
if (offset.getBlockX() == 0 && offset.getBlockY() == 0 && offset.getBlockZ() == 0)
|
||||
return;
|
||||
|
||||
VectorMap<BlockData> b = new VectorMap<>();
|
||||
VectorMap<TileData> s = new VectorMap<>();
|
||||
|
||||
blocks.forEach((vector, data) -> {
|
||||
vector.add(offset);
|
||||
b.put(vector, data);
|
||||
});
|
||||
|
||||
states.forEach((vector, data) -> {
|
||||
vector.add(offset);
|
||||
s.put(vector, data);
|
||||
});
|
||||
|
||||
shrinkOffset = offset;
|
||||
blocks = b;
|
||||
states = s;
|
||||
}
|
||||
|
||||
public void clean() {
|
||||
@@ -1150,8 +1179,8 @@ public class IrisObject extends IrisRegistrant {
|
||||
|
||||
blocks = d;
|
||||
states = dx;
|
||||
writeLock.unlock();
|
||||
shrinkwrap();
|
||||
writeLock.unlock();
|
||||
}
|
||||
|
||||
public void place(Location at) {
|
||||
@@ -1389,4 +1418,10 @@ public class IrisObject extends IrisRegistrant {
|
||||
@Override
|
||||
public void scanForErrors(JSONObject p, VolmitSender sender) {
|
||||
}
|
||||
|
||||
private static class HeaderException extends IOException {
|
||||
public HeaderException() {
|
||||
super("Invalid Header");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.*;
|
||||
import org.bukkit.block.data.type.RedstoneWire;
|
||||
import org.bukkit.block.data.type.Wall;
|
||||
import org.bukkit.block.structure.StructureRotation;
|
||||
import org.bukkit.util.BlockVector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -101,15 +101,15 @@ public class IrisObjectRotation {
|
||||
return e.rotateCopy(this);
|
||||
}
|
||||
|
||||
public IrisJigsawPiece rotateCopy(IrisJigsawPiece v) {
|
||||
public IrisJigsawPiece rotateCopy(IrisJigsawPiece v, IrisPosition offset) {
|
||||
IrisJigsawPiece piece = v.copy();
|
||||
for (IrisJigsawPieceConnector i : piece.getConnectors()) {
|
||||
i.setPosition(rotate(i.getPosition()));
|
||||
i.setPosition(rotate(i.getPosition()).add(offset));
|
||||
i.setDirection(rotate(i.getDirection()));
|
||||
}
|
||||
try {
|
||||
var translate = piece.getPlacementOptions().getTranslate();
|
||||
var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ()));
|
||||
var pos = rotate(new IrisPosition(translate.getX(), translate.getY(), translate.getZ())).add(offset);
|
||||
translate.setX(pos.getX()).setY(pos.getY()).setZ(pos.getZ());
|
||||
} catch (NullPointerException ignored) {}
|
||||
|
||||
@@ -259,14 +259,14 @@ public class IrisObjectRotation {
|
||||
|
||||
g.setRotation(face);
|
||||
|
||||
} else if (d instanceof Orientable) {
|
||||
BlockFace f = getFace(((Orientable) d).getAxis());
|
||||
} else if (d instanceof Orientable g) {
|
||||
BlockFace f = getFace(g.getAxis());
|
||||
BlockVector bv = new BlockVector(f.getModX(), f.getModY(), f.getModZ());
|
||||
bv = rotate(bv.clone(), spinx, spiny, spinz);
|
||||
Axis a = getAxis(bv);
|
||||
|
||||
if (!a.equals(((Orientable) d).getAxis()) && ((Orientable) d).getAxes().contains(a)) {
|
||||
((Orientable) d).setAxis(a);
|
||||
if (!a.equals(g.getAxis()) && g.getAxes().contains(a)) {
|
||||
g.setAxis(a);
|
||||
}
|
||||
} else if (d instanceof MultipleFacing g) {
|
||||
List<BlockFace> faces = new KList<>();
|
||||
@@ -304,14 +304,22 @@ public class IrisObjectRotation {
|
||||
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) {
|
||||
//TODO: Fucks up logs
|
||||
BlockFace f = faceForAxis(g.getAxis());
|
||||
BlockVector bv = new BlockVector(f.getModX(), f.getModY(), f.getModZ());
|
||||
bv = rotate(bv.clone(), spinx, spiny, spinz);
|
||||
BlockFace t = getFace(bv);
|
||||
Axis a = !g.getAxes().contains(Axis.Y) ? axisFor(t) : axisFor2D(t);
|
||||
((Orientable) d).setAxis(a);
|
||||
} else if (d instanceof RedstoneWire wire) {
|
||||
Map<BlockFace, RedstoneWire.Connection> faces = new HashMap<>();
|
||||
|
||||
var allowed = wire.getAllowedFaces();
|
||||
for (BlockFace i : allowed) {
|
||||
RedstoneWire.Connection connection = wire.getFace(i);
|
||||
BlockVector bv = new BlockVector(i.getModX(), i.getModY(), i.getModZ());
|
||||
bv = rotate(bv.clone(), spinx, spiny, spinz);
|
||||
BlockFace r = getFace(bv);
|
||||
if (allowed.contains(r))
|
||||
faces.put(r, connection);
|
||||
}
|
||||
|
||||
for (BlockFace i : allowed) {
|
||||
wire.setFace(i, faces.getOrDefault(i, RedstoneWire.Connection.NONE));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
||||
@@ -63,14 +63,20 @@ public class IrisContext {
|
||||
}
|
||||
}
|
||||
|
||||
public static void dereference() {
|
||||
for (Thread i : context.k()) {
|
||||
if (!i.isAlive() || context.get(i).engine.isClosed()) {
|
||||
if (context.get(i).engine.isClosed()) {
|
||||
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.threadId());
|
||||
}
|
||||
public static synchronized void dereference() {
|
||||
var it = context.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
var entry = it.next();
|
||||
var thread = entry.getKey();
|
||||
var context = entry.getValue();
|
||||
if (thread == null || context == null) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
context.remove(i);
|
||||
if (!thread.isAlive() || context.engine.isClosed()) {
|
||||
Iris.debug("Dereferenced Context<Engine> " + thread.getName() + " " + thread.threadId());
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,7 @@ public class B {
|
||||
|
||||
public static boolean isFoliagePlantable(BlockData d) {
|
||||
return d.getMaterial().equals(Material.GRASS_BLOCK)
|
||||
|| d.getMaterial().equals(Material.MOSS_BLOCK)
|
||||
|| d.getMaterial().equals(Material.ROOTED_DIRT)
|
||||
|| d.getMaterial().equals(Material.DIRT)
|
||||
|| d.getMaterial().equals(Material.COARSE_DIRT)
|
||||
@@ -368,6 +369,7 @@ public class B {
|
||||
|
||||
public static boolean isFoliagePlantable(Material d) {
|
||||
return d.equals(Material.GRASS_BLOCK)
|
||||
|| d.equals(Material.MOSS_BLOCK)
|
||||
|| d.equals(Material.DIRT)
|
||||
|| d.equals(TALL_GRASS)
|
||||
|| d.equals(TALL_SEAGRASS)
|
||||
|
||||
@@ -16,7 +16,7 @@ public interface IrisCustomData extends BlockData {
|
||||
|
||||
static IrisCustomData of(@NotNull BlockData base, @NotNull Identifier custom) {
|
||||
var clazz = base.getClass();
|
||||
var loader = clazz.getClassLoader();
|
||||
var loader = IrisCustomData.class.getClassLoader();
|
||||
return (IrisCustomData) Proxy.newProxyInstance(loader, Internal.getInterfaces(loader, clazz), (proxy, method, args) ->
|
||||
switch (method.getName()) {
|
||||
case "getBase" -> base;
|
||||
@@ -44,15 +44,12 @@ public interface IrisCustomData extends BlockData {
|
||||
|
||||
private static Class<?>[] getInterfaces(ClassLoader loader, Class<?> base) {
|
||||
return cache.computeIfAbsent(base, k -> {
|
||||
Queue<Class<?>> queue = new LinkedList<>();
|
||||
Set<Class<?>> set = new HashSet<>();
|
||||
|
||||
queue.add(k);
|
||||
while (!queue.isEmpty()) {
|
||||
Class<?> i = queue.poll();
|
||||
|
||||
Class<?> i = base;
|
||||
while (i != null) {
|
||||
if (!BlockData.class.isAssignableFrom(i))
|
||||
continue;
|
||||
break;
|
||||
|
||||
for (Class<?> j : i.getInterfaces()) {
|
||||
if (j.isSealed() || j.isHidden())
|
||||
@@ -64,9 +61,7 @@ public interface IrisCustomData extends BlockData {
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
}
|
||||
|
||||
var parent = i.getSuperclass();
|
||||
if (parent != null)
|
||||
queue.add(parent);
|
||||
i = i.getSuperclass();
|
||||
}
|
||||
|
||||
set.add(IrisCustomData.class);
|
||||
|
||||
@@ -20,6 +20,7 @@ package com.volmit.iris.util.format;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.apache.commons.lang.Validate;
|
||||
@@ -495,6 +496,14 @@ public enum C {
|
||||
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
|
||||
}
|
||||
|
||||
public static String strip(final String input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MiniMessage.miniMessage().stripTags(stripColor(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* DyeColor to ChatColor
|
||||
*
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.function.Consumer3;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.mantle.io.IOWorker;
|
||||
@@ -50,6 +51,9 @@ import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
|
||||
@@ -201,6 +205,68 @@ public class Mantle {
|
||||
return get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31);
|
||||
}
|
||||
|
||||
public void getChunks(final int minChunkX,
|
||||
final int maxChunkX,
|
||||
final int minChunkZ,
|
||||
final int maxChunkZ,
|
||||
int parallelism,
|
||||
final Consumer3<Integer, Integer, MantleChunk> consumer
|
||||
) {
|
||||
if (parallelism <= 0) parallelism = 1;
|
||||
final var lock = new Semaphore(parallelism);
|
||||
|
||||
final int minRegionX = minChunkX >> 5;
|
||||
final int maxRegionX = maxChunkX >> 5;
|
||||
final int minRegionZ = minChunkZ >> 5;
|
||||
final int maxRegionZ = maxChunkZ >> 5;
|
||||
|
||||
final int minRelativeX = minChunkX & 31;
|
||||
final int maxRelativeX = maxChunkX & 31;
|
||||
final int minRelativeZ = minChunkZ & 31;
|
||||
final int maxRelativeZ = maxChunkZ & 31;
|
||||
|
||||
final AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
for (int rX = minRegionX; rX <= maxRegionX; rX++) {
|
||||
final int minX = rX == minRegionX ? minRelativeX : 0;
|
||||
final int maxX = rX == maxRegionX ? maxRelativeX : 31;
|
||||
for (int rZ = minRegionZ; rZ <= maxRegionZ; rZ++) {
|
||||
final int minZ = rZ == minRegionZ ? minRelativeZ : 0;
|
||||
final int maxZ = rZ == maxRegionZ ? maxRelativeZ : 31;
|
||||
final int realX = rX << 5;
|
||||
final int realZ = rZ << 5;
|
||||
|
||||
lock.acquireUninterruptibly();
|
||||
final var e = error.get();
|
||||
if (e != null) {
|
||||
if (e instanceof RuntimeException ex) throw ex;
|
||||
else if (e instanceof Error ex) throw ex;
|
||||
else throw new RuntimeException(error.get());
|
||||
}
|
||||
|
||||
getFuture(rX, rZ)
|
||||
.thenAccept(region -> {
|
||||
final MantleChunk zero = region.getOrCreate(0, 0).use();
|
||||
try {
|
||||
for (int xx = minX; xx <= maxX; xx++) {
|
||||
for (int zz = minZ; zz <= maxZ; zz++) {
|
||||
consumer.accept(realX + xx, realZ + zz, region.getOrCreate(xx, zz));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
zero.release();
|
||||
}
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
error.set(ex);
|
||||
return null;
|
||||
})
|
||||
.thenRun(lock::release);
|
||||
}
|
||||
}
|
||||
|
||||
lock.acquireUninterruptibly(parallelism);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag or unflag a chunk
|
||||
*
|
||||
@@ -554,6 +620,53 @@ public class Mantle {
|
||||
return get(x, z);
|
||||
}
|
||||
|
||||
private CompletableFuture<TectonicPlate> getFuture(int x, int z) {
|
||||
final boolean trim = ioTrim.tryAcquire();
|
||||
final boolean unload = ioTectonicUnload.tryAcquire();
|
||||
final Function<TectonicPlate, TectonicPlate> release = p -> {
|
||||
if (trim) ioTrim.release();
|
||||
if (unload) ioTectonicUnload.release();
|
||||
return p;
|
||||
};
|
||||
|
||||
final Supplier<CompletableFuture<TectonicPlate>> fallback = () -> getSafe(x, z)
|
||||
.exceptionally(e -> {
|
||||
if (e instanceof InterruptedException) {
|
||||
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)");
|
||||
Iris.reportError(e);
|
||||
} else {
|
||||
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.thenCompose(p -> {
|
||||
release.apply(p);
|
||||
if (p != null) return CompletableFuture.completedFuture(p);
|
||||
Iris.warn("Retrying to get " + x + " " + z + " Mantle Region");
|
||||
return getFuture(x, z);
|
||||
});
|
||||
|
||||
if (!trim || !unload) {
|
||||
return getSafe(x, z)
|
||||
.thenApply(release)
|
||||
.exceptionallyCompose(e -> {
|
||||
e.printStackTrace();
|
||||
return fallback.get();
|
||||
});
|
||||
}
|
||||
|
||||
Long key = key(x, z);
|
||||
TectonicPlate p = loadedRegions.get(key);
|
||||
if (p != null && !p.isClosed()) {
|
||||
use(key);
|
||||
return CompletableFuture.completedFuture(release.apply(p));
|
||||
}
|
||||
|
||||
return fallback.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This retreives a future of the Tectonic Plate at the given coordinates.
|
||||
* All methods accessing tectonic plates should go through this method
|
||||
@@ -563,8 +676,8 @@ public class Mantle {
|
||||
* @return the future of a tectonic plate.
|
||||
*/
|
||||
@RegionCoordinates
|
||||
private Future<TectonicPlate> getSafe(int x, int z) {
|
||||
return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> {
|
||||
protected CompletableFuture<TectonicPlate> getSafe(int x, int z) {
|
||||
return ioBurst.completableFuture(() -> hyperLock.withResult(x, z, () -> {
|
||||
Long k = key(x, z);
|
||||
use(k);
|
||||
TectonicPlate r = loadedRegions.get(k);
|
||||
|
||||
@@ -131,9 +131,13 @@ public class MantleChunk extends FlaggedChunk {
|
||||
ref.release();
|
||||
}
|
||||
|
||||
public void copyFlags(MantleChunk chunk) {
|
||||
public void copyFrom(MantleChunk chunk) {
|
||||
use();
|
||||
super.copyFlags(chunk);
|
||||
super.copyFrom(chunk, () -> {
|
||||
for (int i = 0; i < sections.length(); i++) {
|
||||
sections.set(i, chunk.get(i));
|
||||
}
|
||||
});
|
||||
release();
|
||||
}
|
||||
|
||||
@@ -195,16 +199,12 @@ public class MantleChunk extends FlaggedChunk {
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public Matter getOrCreate(int section) {
|
||||
Matter matter = get(section);
|
||||
final Matter matter = get(section);
|
||||
if (matter != null) return matter;
|
||||
|
||||
if (matter == null) {
|
||||
matter = new IrisMatter(16, 16, 16);
|
||||
if (!sections.compareAndSet(section, null, matter)) {
|
||||
matter = get(section);
|
||||
}
|
||||
}
|
||||
|
||||
return matter;
|
||||
final Matter instance = new IrisMatter(16, 16, 16);
|
||||
final Matter value = sections.compareAndExchange(section, null, instance);
|
||||
return value == null ? instance : value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -175,10 +175,13 @@ public class TectonicPlate {
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public MantleChunk getOrCreate(int x, int z) {
|
||||
return chunks.updateAndGet(index(x, z), chunk -> {
|
||||
if (chunk != null) return chunk;
|
||||
return new MantleChunk(sectionHeight, x & 31, z & 31);
|
||||
});
|
||||
final int index = index(x, z);
|
||||
final MantleChunk chunk = chunks.get(index);
|
||||
if (chunk != null) return chunk;
|
||||
|
||||
final MantleChunk instance = new MantleChunk(sectionHeight, x & 31, z & 31);
|
||||
final MantleChunk value = chunks.compareAndExchange(index, null, instance);
|
||||
return value == null ? instance : value;
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
|
||||
@@ -56,7 +56,7 @@ public class Bindings {
|
||||
options.setEnvironment(BuildConstants.ENVIRONMENT);
|
||||
options.setBeforeSend((event, hint) -> {
|
||||
if (suppress(event.getThrowable())) return null;
|
||||
event.setTag("iris.safeguard", IrisSafeguard.mode());
|
||||
event.setTag("iris.safeguard", IrisSafeguard.mode().getId());
|
||||
event.setTag("iris.nms", INMS.get().getClass().getCanonicalName());
|
||||
var context = IrisContext.get();
|
||||
if (context != null) event.getContexts().set("engine", context.asContext());
|
||||
@@ -67,6 +67,7 @@ public class Bindings {
|
||||
Sentry.configureScope(scope -> {
|
||||
if (settings.includeServerId) scope.setUser(ServerID.asUser());
|
||||
scope.addAttachment(Attachments.PLUGINS);
|
||||
scope.addAttachment(Attachments.SAFEGUARD);
|
||||
scope.setTag("server", Bukkit.getVersion());
|
||||
scope.setTag("server.type", Bukkit.getName());
|
||||
scope.setTag("server.api", Bukkit.getBukkitVersion());
|
||||
|
||||
@@ -24,6 +24,10 @@ public class AtomicBooleanArray implements Serializable {
|
||||
AA.setVolatile(array, index, newValue);
|
||||
}
|
||||
|
||||
public final boolean getAndSet(int index, boolean newValue) {
|
||||
return (boolean) AA.getAndSet(array, index, newValue);
|
||||
}
|
||||
|
||||
public final boolean compareAndSet(int index, boolean expectedValue, boolean newValue) {
|
||||
return (boolean) AA.compareAndSet(array, index, expectedValue, newValue);
|
||||
}
|
||||
|
||||
@@ -172,6 +172,18 @@ public class MultiBurst implements ExecutorService {
|
||||
return getService().submit(o);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> completableFuture(Callable<T> o) {
|
||||
CompletableFuture<T> f = new CompletableFuture<>();
|
||||
getService().submit(() -> {
|
||||
try {
|
||||
f.complete(o.call());
|
||||
} catch (Exception e) {
|
||||
f.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.volmit.iris.util.sentry;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.volmit.iris.core.safeguard.IrisSafeguard;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import io.sentry.Attachment;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -12,6 +13,7 @@ import java.util.concurrent.Callable;
|
||||
public class Attachments {
|
||||
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
|
||||
public static final Attachment PLUGINS = jsonProvider(Attachments::plugins, "plugins.json");
|
||||
public static final Attachment SAFEGUARD = jsonProvider(IrisSafeguard::asAttachment, "safeguard.json");
|
||||
|
||||
public static Attachment json(Object object, String name) {
|
||||
return new Attachment(GSON.toJson(object).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.volmit.iris.core.safeguard
|
||||
|
||||
import com.volmit.iris.Iris
|
||||
import com.volmit.iris.core.IrisSettings
|
||||
import com.volmit.iris.core.safeguard.task.Diagnostic
|
||||
import com.volmit.iris.core.safeguard.task.Task
|
||||
import com.volmit.iris.core.safeguard.task.ValueWithDiagnostics
|
||||
import com.volmit.iris.core.safeguard.task.tasks
|
||||
import com.volmit.iris.util.format.C
|
||||
import com.volmit.iris.util.scheduling.J
|
||||
import org.bukkit.Bukkit
|
||||
import java.util.*
|
||||
|
||||
object IrisSafeguard {
|
||||
@Volatile
|
||||
private var forceShutdown = false
|
||||
private var results: Map<Task, ValueWithDiagnostics<Mode>> = emptyMap()
|
||||
private var context: Map<String, String> = emptyMap()
|
||||
private var attachment: Map<String, List<String>> = emptyMap()
|
||||
private var mode = Mode.STABLE
|
||||
private var count = 0
|
||||
|
||||
@JvmStatic
|
||||
fun execute() {
|
||||
val results = LinkedHashMap<Task, ValueWithDiagnostics<Mode>>(tasks.size)
|
||||
val context = LinkedHashMap<String, String>(tasks.size)
|
||||
val attachment = LinkedHashMap<String, List<String>>(tasks.size)
|
||||
var mode = Mode.STABLE
|
||||
var count = 0
|
||||
for (task in tasks) {
|
||||
var result: ValueWithDiagnostics<Mode>
|
||||
try {
|
||||
result = task.run()
|
||||
} catch (e: Throwable) {
|
||||
Iris.reportError(e)
|
||||
result = ValueWithDiagnostics(
|
||||
Mode.WARNING,
|
||||
Diagnostic(Diagnostic.Logger.ERROR, "Error while running task ${task.id}", e)
|
||||
)
|
||||
}
|
||||
mode = mode.highest(result.value)
|
||||
results[task] = result
|
||||
context[task.id] = result.value.id
|
||||
attachment[task.id] = result.diagnostics.flatMap { it.toString().split('\n') }
|
||||
if (result.value != Mode.STABLE) count++
|
||||
}
|
||||
|
||||
this.results = Collections.unmodifiableMap(results)
|
||||
this.context = Collections.unmodifiableMap(context)
|
||||
this.attachment = Collections.unmodifiableMap(attachment)
|
||||
this.mode = mode
|
||||
this.count = count
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun mode() = mode
|
||||
|
||||
@JvmStatic
|
||||
fun asContext() = context
|
||||
|
||||
@JvmStatic
|
||||
fun asAttachment() = attachment
|
||||
|
||||
@JvmStatic
|
||||
fun splash() {
|
||||
Iris.instance.splash()
|
||||
printReports()
|
||||
printFooter()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun printReports() {
|
||||
when (mode) {
|
||||
Mode.STABLE -> Iris.info(C.BLUE.toString() + "0 Conflicts found")
|
||||
Mode.WARNING -> Iris.warn(C.GOLD.toString() + "%s Issues found", count)
|
||||
Mode.UNSTABLE -> Iris.error(C.DARK_RED.toString() + "%s Issues found", count)
|
||||
}
|
||||
|
||||
results.values.forEach { it.log(withStackTrace = true) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun printFooter() {
|
||||
when (mode) {
|
||||
Mode.STABLE -> Iris.info(C.BLUE.toString() + "Iris is running Stable")
|
||||
Mode.WARNING -> warning()
|
||||
Mode.UNSTABLE -> unstable()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isForceShutdown() = forceShutdown
|
||||
|
||||
private fun warning() {
|
||||
Iris.warn(C.GOLD.toString() + "Iris is running in Warning Mode")
|
||||
|
||||
Iris.warn("")
|
||||
Iris.warn(C.DARK_GRAY.toString() + "--==<" + C.GOLD + " IMPORTANT " + C.DARK_GRAY + ">==--")
|
||||
Iris.warn(C.GOLD.toString() + "Iris is running in warning mode which may cause the following issues:")
|
||||
Iris.warn("- Data Loss")
|
||||
Iris.warn("- Errors")
|
||||
Iris.warn("- Broken worlds")
|
||||
Iris.warn("- Unexpected behavior.")
|
||||
Iris.warn("- And perhaps further complications.")
|
||||
Iris.warn("")
|
||||
}
|
||||
|
||||
private fun unstable() {
|
||||
Iris.error(C.DARK_RED.toString() + "Iris is running in Unstable Mode")
|
||||
|
||||
Iris.error("")
|
||||
Iris.error(C.DARK_GRAY.toString() + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--")
|
||||
Iris.error("Iris is running in unstable mode which may cause the following issues:")
|
||||
Iris.error(C.DARK_RED.toString() + "Server Issues")
|
||||
Iris.error("- Server won't boot")
|
||||
Iris.error("- Data Loss")
|
||||
Iris.error("- Unexpected behavior.")
|
||||
Iris.error("- And More...")
|
||||
Iris.error(C.DARK_RED.toString() + "World Issues")
|
||||
Iris.error("- Worlds can't load due to corruption.")
|
||||
Iris.error("- Worlds may slowly corrupt until they can't load.")
|
||||
Iris.error("- World data loss.")
|
||||
Iris.error("- And More...")
|
||||
Iris.error(C.DARK_RED.toString() + "ATTENTION: " + C.RED + "While running Iris in unstable mode, you won't be eligible for support.")
|
||||
|
||||
if (IrisSettings.get().general.isDoomsdayAnnihilationSelfDestructMode) {
|
||||
Iris.error(C.DARK_RED.toString() + "Boot Unstable is set to true, continuing with the startup process in 10 seconds.")
|
||||
J.sleep(10000L)
|
||||
} else {
|
||||
Iris.error(C.DARK_RED.toString() + "Go to plugins/iris/settings.json and set DoomsdayAnnihilationSelfDestructMode to true if you wish to proceed.")
|
||||
Iris.error(C.DARK_RED.toString() + "The server will shutdown in 10 seconds.")
|
||||
J.sleep(10000L)
|
||||
Iris.error(C.DARK_RED.toString() + "Shutting down server.")
|
||||
forceShutdown = true
|
||||
try {
|
||||
Bukkit.getPluginManager().disablePlugins()
|
||||
} finally {
|
||||
Runtime.getRuntime().halt(42)
|
||||
}
|
||||
}
|
||||
Iris.info("")
|
||||
}
|
||||
}
|
||||
76
core/src/main/kotlin/com/volmit/iris/core/safeguard/Mode.kt
Normal file
76
core/src/main/kotlin/com/volmit/iris/core/safeguard/Mode.kt
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.volmit.iris.core.safeguard
|
||||
|
||||
import com.volmit.iris.BuildConstants
|
||||
import com.volmit.iris.Iris
|
||||
import com.volmit.iris.core.IrisSettings
|
||||
import com.volmit.iris.util.format.C
|
||||
import com.volmit.iris.util.format.Form
|
||||
|
||||
enum class Mode(private val color: C) {
|
||||
STABLE(C.IRIS),
|
||||
WARNING(C.GOLD),
|
||||
UNSTABLE(C.RED);
|
||||
|
||||
val id = name.lowercase()
|
||||
|
||||
fun highest(m: Mode): Mode {
|
||||
return if (m.ordinal > ordinal) m else this
|
||||
}
|
||||
|
||||
fun tag(subTag: String?): String {
|
||||
if (subTag == null || subTag.isBlank()) return wrap("Iris") + C.GRAY + ": "
|
||||
return wrap("Iris") + " " + wrap(subTag) + C.GRAY + ": "
|
||||
}
|
||||
|
||||
private fun wrap(tag: String?): String {
|
||||
return C.BOLD.toString() + "" + C.DARK_GRAY + "[" + C.BOLD + color + tag + C.BOLD + C.DARK_GRAY + "]" + C.RESET
|
||||
}
|
||||
|
||||
fun trySplash() {
|
||||
if (!IrisSettings.get().general.isSplashLogoStartup) return
|
||||
splash()
|
||||
}
|
||||
|
||||
fun splash() {
|
||||
val padd = Form.repeat(" ", 8)
|
||||
val padd2 = Form.repeat(" ", 4)
|
||||
|
||||
val splash = arrayOf(
|
||||
padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@",
|
||||
padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + color + " .(((()))). ",
|
||||
padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + color + " .((((((())))))). ",
|
||||
padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + color + " ((((((((())))))))) " + C.GRAY + " @",
|
||||
padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + color + " ((((((((-))))))))) " + C.GRAY + " @@",
|
||||
padd + C.GRAY + "@@@&&" + color + " ((((((({ })))))))) " + C.GRAY + " &&@@@",
|
||||
padd + C.GRAY + "@@" + color + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@",
|
||||
padd + C.GRAY + "@" + color + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + color + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@",
|
||||
padd + C.GRAY + "" + color + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@",
|
||||
padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@",
|
||||
)
|
||||
|
||||
val info = arrayOf(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
padd2 + color + " Iris",
|
||||
padd2 + C.GRAY + " by " + color + "Volmit Software",
|
||||
padd2 + C.GRAY + " v" + color + Iris.instance.description.version,
|
||||
padd2 + C.GRAY + " c" + color + BuildConstants.COMMIT + C.GRAY + "/" + color + BuildConstants.ENVIRONMENT,
|
||||
)
|
||||
|
||||
|
||||
val builder = StringBuilder("\n\n")
|
||||
for (i in splash.indices) {
|
||||
builder.append(splash[i])
|
||||
if (i < info.size) {
|
||||
builder.append(info[i])
|
||||
}
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
Iris.info(builder.toString())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.volmit.iris.core.safeguard.task
|
||||
|
||||
import com.volmit.iris.core.safeguard.Mode
|
||||
import com.volmit.iris.util.format.Form
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
abstract class Task(
|
||||
val id: String,
|
||||
val name: String = Form.capitalizeWords(id.replace(" ", "_").lowercase()),
|
||||
) {
|
||||
|
||||
abstract fun run(): ValueWithDiagnostics<Mode>
|
||||
|
||||
companion object {
|
||||
fun of(id: String, name: String = id, action: () -> ValueWithDiagnostics<Mode>) = object : Task(id, name) {
|
||||
override fun run() = action()
|
||||
}
|
||||
|
||||
fun of(id: String, action: () -> ValueWithDiagnostics<Mode>) = object : Task(id) {
|
||||
override fun run() = action()
|
||||
}
|
||||
|
||||
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
||||
ReadOnlyProperty { _, property -> of(property.name, action) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.volmit.iris.core.safeguard.task
|
||||
|
||||
import com.volmit.iris.Iris
|
||||
import com.volmit.iris.core.IrisWorlds
|
||||
import com.volmit.iris.core.nms.INMS
|
||||
import com.volmit.iris.core.nms.v1X.NMSBinding1X
|
||||
import com.volmit.iris.core.safeguard.Mode
|
||||
import com.volmit.iris.core.safeguard.Mode.*
|
||||
import com.volmit.iris.core.safeguard.task.Diagnostic.Logger.*
|
||||
import com.volmit.iris.core.safeguard.task.Task.Companion.of
|
||||
import com.volmit.iris.util.agent.Agent
|
||||
import com.volmit.iris.util.misc.getHardware
|
||||
import org.bukkit.Bukkit
|
||||
import java.util.stream.Collectors
|
||||
import javax.tools.ToolProvider
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
private val memory by task {
|
||||
val mem = getHardware.getProcessMemory()
|
||||
if (mem >= 5999) STABLE.withDiagnostics()
|
||||
else STABLE.withDiagnostics(
|
||||
WARN.create("Low Memory"),
|
||||
WARN.create("- 6GB+ Ram is recommended"),
|
||||
WARN.create("- Process Memory: $mem MB")
|
||||
)
|
||||
}
|
||||
|
||||
private val incompatibilities by task {
|
||||
val plugins = mutableSetOf("dynmap", "Stratos")
|
||||
plugins.removeIf { server.pluginManager.getPlugin(it) == null }
|
||||
|
||||
if (plugins.isEmpty()) STABLE.withDiagnostics()
|
||||
else {
|
||||
val diagnostics = mutableListOf<Diagnostic>()
|
||||
if ("dynmap" in plugins) diagnostics.addAll(
|
||||
ERROR.create("Dynmap"),
|
||||
ERROR.create("- The plugin Dynmap is not compatible with the server."),
|
||||
ERROR.create("- If you want to have a map plugin like Dynmap, consider Bluemap.")
|
||||
)
|
||||
if ("Stratos" in plugins) diagnostics.addAll(
|
||||
ERROR.create("Stratos"),
|
||||
ERROR.create("- Iris is not compatible with other worldgen plugins.")
|
||||
)
|
||||
WARNING.withDiagnostics(diagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
private val software by task {
|
||||
val supported = setOf(
|
||||
"purpur",
|
||||
"pufferfish",
|
||||
"paper",
|
||||
"spigot",
|
||||
"bukkit"
|
||||
)
|
||||
|
||||
if (supported.any { server.name.contains(it, true) }) STABLE.withDiagnostics()
|
||||
else WARNING.withDiagnostics(
|
||||
WARN.create("Unsupported Server Software"),
|
||||
WARN.create("- Please consider using Paper or Purpur instead.")
|
||||
)
|
||||
}
|
||||
|
||||
private val version by task {
|
||||
val parts = Iris.instance.description.version.split('-')
|
||||
val minVersion = parts[1]
|
||||
val maxVersion = parts[2]
|
||||
|
||||
if (INMS.get() !is NMSBinding1X) STABLE.withDiagnostics()
|
||||
else UNSTABLE.withDiagnostics(
|
||||
ERROR.create("Server Version"),
|
||||
ERROR.create("- Iris only supports $minVersion > $maxVersion")
|
||||
)
|
||||
}
|
||||
|
||||
private val injection by task {
|
||||
if (!Agent.install()) UNSTABLE.withDiagnostics(
|
||||
ERROR.create("Java Agent"),
|
||||
ERROR.create("- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments."),
|
||||
ERROR.create("- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.path)
|
||||
)
|
||||
else if (!INMS.get().injectBukkit()) UNSTABLE.withDiagnostics(
|
||||
ERROR.create("Code Injection"),
|
||||
ERROR.create("- Failed to inject code. Please contact support")
|
||||
)
|
||||
else STABLE.withDiagnostics()
|
||||
}
|
||||
|
||||
private val dimensionTypes by task {
|
||||
val keys = IrisWorlds.get()
|
||||
.dimensions
|
||||
.map { it.dimensionTypeKey }
|
||||
.collect(Collectors.toSet())
|
||||
|
||||
if (!INMS.get().missingDimensionTypes(*keys.toTypedArray())) STABLE.withDiagnostics()
|
||||
else UNSTABLE.withDiagnostics(
|
||||
ERROR.create("Dimension Types"),
|
||||
ERROR.create("- Required Iris dimension types were not loaded."),
|
||||
ERROR.create("- If this still happens after a restart please contact support.")
|
||||
)
|
||||
}
|
||||
|
||||
private val diskSpace by task {
|
||||
if (server.worldContainer.freeSpace.toDouble().div(0x4000_0000) > 3) STABLE.withDiagnostics()
|
||||
else WARNING.withDiagnostics(
|
||||
WARN.create("Insufficient Disk Space"),
|
||||
WARN.create("- 3GB of free space is required for Iris to function.")
|
||||
)
|
||||
}
|
||||
|
||||
private val java by task {
|
||||
val version = Iris.getJavaVersion()
|
||||
val jdk = runCatching { ToolProvider.getSystemJavaCompiler() }.getOrNull() != null
|
||||
if (version in setOf(21) && jdk) STABLE.withDiagnostics()
|
||||
else WARNING.withDiagnostics(
|
||||
WARN.create("Unsupported Java version"),
|
||||
WARN.create("- Please consider using JDK 21 Instead of ${if(jdk) "JDK" else "JRE"} $version")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
val tasks = listOf(
|
||||
memory,
|
||||
incompatibilities,
|
||||
software,
|
||||
version,
|
||||
injection,
|
||||
dimensionTypes,
|
||||
diskSpace,
|
||||
java,
|
||||
)
|
||||
|
||||
private val server get() = Bukkit.getServer()
|
||||
private fun <T> MutableList<T>.addAll(vararg values: T) = values.forEach(this::add)
|
||||
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
||||
ReadOnlyProperty { _, property -> of(property.name, action) }
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.volmit.iris.core.safeguard.task
|
||||
|
||||
import com.volmit.iris.Iris
|
||||
import com.volmit.iris.util.format.C
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
|
||||
data class ValueWithDiagnostics<out T>(
|
||||
val value: T,
|
||||
val diagnostics: List<Diagnostic>
|
||||
) {
|
||||
constructor(value: T, vararg diagnostics: Diagnostic) : this(value, diagnostics.toList())
|
||||
|
||||
@JvmOverloads
|
||||
fun log(
|
||||
withException: Boolean = true,
|
||||
withStackTrace: Boolean = false
|
||||
) {
|
||||
diagnostics.forEach { it.log(withException, withStackTrace) }
|
||||
}
|
||||
}
|
||||
|
||||
data class Diagnostic @JvmOverloads constructor(
|
||||
val logger: Logger = Logger.ERROR,
|
||||
val message: String,
|
||||
val exception: Throwable? = null
|
||||
) {
|
||||
|
||||
enum class Logger(
|
||||
private val logger: (String) -> Unit
|
||||
) {
|
||||
DEBUG(Iris::debug),
|
||||
RAW(Iris::msg),
|
||||
INFO(Iris::info),
|
||||
WARN(Iris::warn),
|
||||
ERROR(Iris::error);
|
||||
|
||||
fun print(message: String) = message.split('\n').forEach(logger)
|
||||
fun create(message: String, exception: Throwable? = null) = Diagnostic(this, message, exception)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun log(
|
||||
withException: Boolean = true,
|
||||
withStackTrace: Boolean = false
|
||||
) {
|
||||
logger.print(render(withException, withStackTrace))
|
||||
}
|
||||
|
||||
fun render(
|
||||
withException: Boolean = true,
|
||||
withStackTrace: Boolean = false
|
||||
): String = buildString {
|
||||
append(message)
|
||||
if (withException && exception != null) {
|
||||
append(": ")
|
||||
append(exception)
|
||||
if (withStackTrace) {
|
||||
ByteArrayOutputStream().use { os ->
|
||||
val ps = PrintStream(os)
|
||||
exception.printStackTrace(ps)
|
||||
ps.flush()
|
||||
append("\n")
|
||||
append(os.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = C.strip(render())
|
||||
}
|
||||
|
||||
fun <T> T.withDiagnostics(vararg diagnostics: Diagnostic) = ValueWithDiagnostics(this, diagnostics.toList())
|
||||
fun <T> T.withDiagnostics(diagnostics: List<Diagnostic>) = ValueWithDiagnostics(this, diagnostics)
|
||||
@@ -6,14 +6,9 @@ import com.volmit.iris.engine.framework.Engine
|
||||
import com.volmit.iris.util.context.ChunkContext
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates
|
||||
import com.volmit.iris.util.mantle.Mantle
|
||||
import com.volmit.iris.util.mantle.MantleChunk
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag
|
||||
import com.volmit.iris.util.parallel.MultiBurst
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
@@ -27,51 +22,49 @@ interface MatterGenerator {
|
||||
|
||||
@ChunkCoordinates
|
||||
fun generateMatter(x: Int, z: Int, multicore: Boolean, context: ChunkContext) {
|
||||
if (!engine.dimension.isUseMantle || mantle.hasFlag(x, z, MantleFlag.PLANNED))
|
||||
return
|
||||
if (!engine.dimension.isUseMantle) return
|
||||
val multicore = multicore || IrisSettings.get().generator.isUseMulticoreMantle
|
||||
|
||||
mantle.write(engine.mantle, x, z, radius, multicore).use { writer ->
|
||||
for (pair in components) {
|
||||
radius(x, z, pair.b, { x, z ->
|
||||
for (c in pair.a) {
|
||||
emit(Triple(x, z, c))
|
||||
}
|
||||
}, { (x, z, c) -> launch(multicore) {
|
||||
acquireChunk(multicore, writer, x, z)
|
||||
.raiseFlagSuspend(MantleFlag.PLANNED, c.flag) {
|
||||
c.generateLayer(writer, x, z, context)
|
||||
runBlocking {
|
||||
radius(x, z, pair.b) { x, z ->
|
||||
val mc = writer.acquireChunk(x, z)
|
||||
if (mc.isFlagged(MantleFlag.PLANNED))
|
||||
return@radius
|
||||
|
||||
for (c in pair.a) {
|
||||
if (mc.isFlagged(c.flag))
|
||||
continue
|
||||
|
||||
launch(multicore) {
|
||||
mc.raiseFlagSuspend(c.flag) {
|
||||
c.generateLayer(writer, x, z, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
radius(x, z, realRadius, { x, z ->
|
||||
emit(Pair(x, z))
|
||||
}, {
|
||||
writer.acquireChunk(it.a, it.b)
|
||||
radius(x, z, realRadius) { x, z ->
|
||||
writer.acquireChunk(x, z)
|
||||
.flag(MantleFlag.PLANNED, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> radius(x: Int, z: Int, radius: Int, collector: suspend FlowCollector<T>.(Int, Int) -> Unit, task: suspend CoroutineScope.(T) -> Unit) = runBlocking {
|
||||
flow {
|
||||
for (i in -radius..radius) {
|
||||
for (j in -radius..radius) {
|
||||
collector(x + i, z + j)
|
||||
}
|
||||
private inline fun radius(x: Int, z: Int, radius: Int, crossinline task: (Int, Int) -> Unit) {
|
||||
for (i in -radius..radius) {
|
||||
for (j in -radius..radius) {
|
||||
task(x + i, z + j)
|
||||
}
|
||||
}.collect { task(it) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val dispatcher = MultiBurst.burst.dispatcher
|
||||
private val dispatcher = MultiBurst.burst.dispatcher//.limitedParallelism(128, "Mantle")
|
||||
private fun CoroutineScope.launch(multicore: Boolean, block: suspend CoroutineScope.() -> Unit) =
|
||||
launch(if (multicore) dispatcher else EmptyCoroutineContext, block = block)
|
||||
|
||||
private suspend fun CoroutineScope.acquireChunk(multicore: Boolean, writer: MantleWriter, x: Int, z: Int): MantleChunk {
|
||||
return if (multicore) async(Dispatchers.IO) { writer.acquireChunk(x, z) }.await()
|
||||
else writer.acquireChunk(x, z)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ class ChunkedDataCache<T> private constructor(
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun get(x: Int, z: Int): T? {
|
||||
if (!cache) {
|
||||
return stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.volmit.iris.util.mantle
|
||||
import com.volmit.iris.util.data.Varint
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag
|
||||
import com.volmit.iris.util.parallel.AtomicBooleanArray
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.io.DataInput
|
||||
@@ -22,9 +25,21 @@ abstract class FlaggedChunk() {
|
||||
|
||||
abstract fun isClosed(): Boolean
|
||||
|
||||
protected fun copyFlags(other: FlaggedChunk) {
|
||||
protected fun copyFrom(other: FlaggedChunk, action: Runnable) = runBlocking {
|
||||
coroutineScope {
|
||||
for (i in 0 until flags.length()) {
|
||||
launch { locks[i].lock() }.start()
|
||||
}
|
||||
}
|
||||
|
||||
action.run()
|
||||
|
||||
for (i in 0 until flags.length()) {
|
||||
flags.set(i, other.flags.get(i))
|
||||
flags[i] = other.flags[i]
|
||||
}
|
||||
|
||||
for (i in 0 until flags.length()) {
|
||||
locks[i].unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,19 +49,15 @@ abstract class FlaggedChunk() {
|
||||
flags.set(flag.ordinal(), value)
|
||||
}
|
||||
|
||||
suspend fun raiseFlagSuspend(guard: MantleFlag?, flag: MantleFlag, task: suspend () -> Unit) {
|
||||
suspend fun raiseFlagSuspend(flag: MantleFlag, task: suspend () -> Unit) {
|
||||
if (isClosed()) throw IllegalStateException("Chunk is closed!")
|
||||
if (guard != null && isFlagged(guard)) return
|
||||
if (isFlagged(flag)) return
|
||||
|
||||
locks[flag.ordinal()].withLock {
|
||||
if (flags.compareAndSet(flag.ordinal(), false, true)) {
|
||||
try {
|
||||
task()
|
||||
} catch (e: Throwable) {
|
||||
flags.set(flag.ordinal(), false)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
if (isFlagged(flag)) return
|
||||
task()
|
||||
if (flags.getAndSet(flag.ordinal(), true))
|
||||
throw IllegalStateException("Flag ${flag.name()} was already set after task ran!")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,13 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||
@@ -98,17 +94,10 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
@@ -656,7 +645,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1205;
|
||||
return DataVersion.V1_20_5;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R1;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
@@ -43,7 +35,6 @@ import com.volmit.iris.util.nbt.mca.palette.*;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
@@ -106,17 +97,10 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
@@ -671,7 +655,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1205;
|
||||
return DataVersion.V1_20_5;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,13 +6,9 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.volmit.iris.core.link.Identifier;
|
||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
@@ -100,17 +96,10 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
@@ -665,7 +654,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
return DataVersion.V1_21_3;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,13 +11,11 @@ import com.volmit.iris.core.nms.container.BlockProperty;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||
import com.volmit.iris.util.agent.Agent;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
@@ -66,7 +64,6 @@ import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
@@ -100,13 +97,8 @@ import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
@@ -661,7 +653,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
return DataVersion.V1_21_3;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -60,7 +60,6 @@ import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||
@@ -654,7 +653,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
return DataVersion.V1_21_3;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -653,7 +653,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
return DataVersion.V1_21_3;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,8 +46,6 @@ import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.RandomSequences;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.CustomSpawner;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
@@ -89,7 +87,6 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -655,7 +652,7 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1213;
|
||||
return DataVersion.V1_21_3;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R7;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisBiome;
|
||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.CraftWorld;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CustomBiomeSource extends BiomeSource {
|
||||
|
||||
private final long seed;
|
||||
private final Engine engine;
|
||||
private final Registry<Biome> biomeCustomRegistry;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||
private final RNG rng;
|
||||
private final KMap<String, Holder<Biome>> customBiomes;
|
||||
|
||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
||||
this.engine = engine;
|
||||
this.seed = seed;
|
||||
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
|
||||
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
|
||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
||||
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
||||
}
|
||||
|
||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
||||
List<Holder<Biome>> b = new ArrayList<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
if (i.isCustom()) {
|
||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||
b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry
|
||||
.getValue(Identifier.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()))).get()).get());
|
||||
}
|
||||
} else {
|
||||
b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private static Object getFor(Class<?> type, Object source) {
|
||||
Object o = fieldFor(type, source);
|
||||
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
|
||||
return invokeFor(type, source);
|
||||
}
|
||||
|
||||
private static Object fieldFor(Class<?> returns, Object in) {
|
||||
return fieldForClass(returns, in.getClass(), in);
|
||||
}
|
||||
|
||||
private static Object invokeFor(Class<?> returns, Object in) {
|
||||
for (Method i : in.getClass().getMethods()) {
|
||||
if (i.getReturnType().equals(returns)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||
return i.invoke(in);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||
for (Field i : sourceType.getDeclaredFields()) {
|
||||
if (i.getType().equals(returnType)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||
return (T) i.get(in);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
||||
return getAllBiomes(
|
||||
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
||||
.lookup(Registries.BIOME).orElse(null),
|
||||
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
|
||||
engine).stream();
|
||||
}
|
||||
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
||||
KMap<String, Holder<Biome>> m = new KMap<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
if (i.isCustom()) {
|
||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
||||
Identifier resourceLocation = Identifier.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId());
|
||||
Biome biome = customRegistry.getValue(resourceLocation);
|
||||
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
|
||||
if (optionalBiomeKey.isEmpty()) {
|
||||
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
|
||||
continue;
|
||||
}
|
||||
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
|
||||
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(biomeKey);
|
||||
if (optionalReferenceHolder.isEmpty()) {
|
||||
Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName());
|
||||
continue;
|
||||
}
|
||||
m.put(j.getId(), optionalReferenceHolder.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private RegistryAccess registry() {
|
||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BiomeSource> codec() {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||
int m = (y - engine.getMinHeight()) << 2;
|
||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
||||
if (ib.isCustom()) {
|
||||
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
||||
} else {
|
||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
||||
return NMSBinding.biomeToBiomeBase(biomeRegistry, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R7;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.ResultLocator;
|
||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.engine.object.IrisStructurePopulator;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.collection.KSet;
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.reflect.WrappedField;
|
||||
import com.volmit.iris.util.reflect.WrappedReturningMethod;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.tags.StructureTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.random.WeightedList;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.*;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.generator.CustomChunkGenerator;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.generator.structure.CraftStructure;
|
||||
import org.bukkit.event.world.AsyncStructureSpawnEvent;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||
private final IrisStructurePopulator populator;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
this.delegate = delegate;
|
||||
this.engine = engine;
|
||||
this.populator = new IrisStructurePopulator(engine);
|
||||
var dimension = engine.getDimension();
|
||||
|
||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
||||
addAll(dimension.getJigsawStructures(), placements);
|
||||
for (var region : dimension.getAllRegions(engine)) {
|
||||
addAll(region.getJigsawStructures(), placements);
|
||||
for (var biome : region.getAllBiomes(engine))
|
||||
addAll(biome.getJigsawStructures(), placements);
|
||||
}
|
||||
var stronghold = dimension.getStronghold();
|
||||
if (stronghold != null)
|
||||
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
||||
placements.removeIf(Objects::isNull);
|
||||
|
||||
var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow();
|
||||
for (var s : placements) {
|
||||
try {
|
||||
String raw = s.getStructureKey();
|
||||
if (raw == null) continue;
|
||||
boolean tag = raw.startsWith("#");
|
||||
if (tag) raw = raw.substring(1);
|
||||
|
||||
var location = Identifier.parse(raw);
|
||||
if (!tag) {
|
||||
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = TagKey.create(Registries.STRUCTURE, location);
|
||||
var set = registry.get(key).orElse(null);
|
||||
if (set == null) {
|
||||
Iris.error("Could not find structure tag: " + raw);
|
||||
continue;
|
||||
}
|
||||
for (var holder : set) {
|
||||
var resourceKey = holder.unwrapKey().orElse(null);
|
||||
if (resourceKey == null) continue;
|
||||
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to load structure: " + s.getLoadKey());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
||||
if (placements == null) return;
|
||||
placements.stream()
|
||||
.map(IrisJigsawStructurePlacement::getStructure)
|
||||
.map(engine.getData().getJigsawStructureLoader()::load)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(structures::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
||||
if (holders.size() == 0) return null;
|
||||
if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
|
||||
var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
|
||||
return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
|
||||
}
|
||||
if (engine.getDimension().isDisableExplorerMaps())
|
||||
return null;
|
||||
|
||||
KMap<String, Holder<Structure>> structures = new KMap<>();
|
||||
for (var holder : holders) {
|
||||
if (holder == null) continue;
|
||||
var key = holder.unwrapKey().orElse(null);
|
||||
var set = this.structures.get(key);
|
||||
if (set == null) continue;
|
||||
for (var structure : set) {
|
||||
structures.put(structure, holder);
|
||||
}
|
||||
}
|
||||
if (structures.isEmpty())
|
||||
return null;
|
||||
|
||||
var locator = ResultLocator.locateStructure(structures.keySet())
|
||||
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
||||
if (findUnexplored)
|
||||
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
||||
|
||||
try {
|
||||
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
||||
if (result == null) return null;
|
||||
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
||||
return Pair.of(blockPos, result.obj());
|
||||
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends ChunkGenerator> codec() {
|
||||
return MapCodec.unit(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator getDelegate() {
|
||||
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
||||
return chunkGenerator.getDelegate();
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return delegate.getMinY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return delegate.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
|
||||
if (!structureManager.shouldGenerateStructures())
|
||||
return;
|
||||
var chunkPos = access.getPos();
|
||||
var sectionPos = SectionPos.bottomOf(access);
|
||||
var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE);
|
||||
populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> {
|
||||
var loc = Identifier.tryParse(key);
|
||||
if (loc == null) return false;
|
||||
var holder = registry.get(loc).orElse(null);
|
||||
if (holder == null) return false;
|
||||
var structure = holder.value();
|
||||
var biomes = structure.biomes();
|
||||
|
||||
var start = structure.generate(
|
||||
holder,
|
||||
levelKey,
|
||||
registryAccess,
|
||||
this,
|
||||
biomeSource,
|
||||
structureState.randomState(),
|
||||
templateManager,
|
||||
structureState.getLevelSeed(),
|
||||
chunkPos,
|
||||
fetchReferences(structureManager, access, sectionPos, structure),
|
||||
access,
|
||||
biome -> ignoreBiomes || biomes.contains(biome)
|
||||
);
|
||||
|
||||
if (!start.isValid())
|
||||
return false;
|
||||
|
||||
BoundingBox box = start.getBoundingBox();
|
||||
AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent(
|
||||
structureManager.level.getMinecraftWorld().getWorld(),
|
||||
CraftStructure.minecraftToBukkit(structure),
|
||||
new org.bukkit.util.BoundingBox(
|
||||
box.minX(),
|
||||
box.minY(),
|
||||
box.minZ(),
|
||||
box.maxX(),
|
||||
box.maxY(),
|
||||
box.maxZ()
|
||||
), chunkPos.x, chunkPos.z);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (!event.isCancelled()) {
|
||||
structureManager.setStartForStructure(sectionPos, structure, start, access);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) {
|
||||
StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access);
|
||||
return structurestart != null ? structurestart.getReferences() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) {
|
||||
return delegate.createState(holderlookup, randomstate, i, conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
||||
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
||||
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
||||
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
|
||||
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||
if (!structureManager.shouldGenerateStructures())
|
||||
return;
|
||||
|
||||
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
|
||||
BlockPos blockPos = sectionPos.origin();
|
||||
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
var list = structures.stream()
|
||||
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||
.toList();
|
||||
|
||||
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
int wX = x + blockPos.getX();
|
||||
int wZ = z + blockPos.getZ();
|
||||
|
||||
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
Structure structure = list.get(j);
|
||||
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||
|
||||
try {
|
||||
level.setCurrentlyGenerating(supplier);
|
||||
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||
} catch (Exception exception) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||
category.setDetail("Description", supplier::get);
|
||||
throw new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
|
||||
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
|
||||
}
|
||||
|
||||
private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) {
|
||||
ChunkPos chunkPos = ichunkaccess.getPos();
|
||||
int minX = chunkPos.getMinBlockX();
|
||||
int minZ = chunkPos.getMinBlockZ();
|
||||
LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration();
|
||||
int minY = heightAccessor.getMinY() + 1;
|
||||
int maxY = heightAccessor.getMaxY();
|
||||
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
||||
return delegate.getSpawnHeight(levelheightaccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenDepth() {
|
||||
return delegate.getGenDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||
int block = engine.getHeight(i, j, true);
|
||||
int water = engine.getHeight(i, j, false);
|
||||
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
|
||||
for (int k = 0; k < column.length; k++) {
|
||||
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
|
||||
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
|
||||
else column[k] = Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
return new NoiseColumn(levelheightaccessor.getMinY(), column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
|
||||
return delegate.getTypeNameForDataFixer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
delegate.validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> holder) {
|
||||
return delegate.getBiomeGenerationSettings(holder);
|
||||
}
|
||||
|
||||
static {
|
||||
Field biomeSource = null;
|
||||
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
||||
if (!field.getType().equals(BiomeSource.class))
|
||||
continue;
|
||||
biomeSource = field;
|
||||
break;
|
||||
}
|
||||
if (biomeSource == null)
|
||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
||||
|
||||
Method setHeight = null;
|
||||
for (Method method : Heightmap.class.getDeclaredMethods()) {
|
||||
var types = method.getParameterTypes();
|
||||
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|
||||
|| !method.getReturnType().equals(void.class))
|
||||
continue;
|
||||
setHeight = method;
|
||||
break;
|
||||
}
|
||||
if (setHeight == null)
|
||||
throw new RuntimeException("Could not find setHeight method in Heightmap!");
|
||||
|
||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
|
||||
}
|
||||
|
||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
||||
try {
|
||||
BIOME_SOURCE.set(generator, source);
|
||||
if (generator instanceof CustomChunkGenerator custom)
|
||||
BIOME_SOURCE.set(custom.getDelegate(), source);
|
||||
|
||||
return generator;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,856 @@
|
||||
package com.volmit.iris.core.nms.v1_21_R7;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.nms.INMSBinding;
|
||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||
import com.volmit.iris.core.nms.container.Pair;
|
||||
import com.volmit.iris.core.nms.container.StructurePlacement;
|
||||
import com.volmit.iris.core.nms.container.BlockProperty;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||
import com.volmit.iris.util.agent.Agent;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
import com.volmit.iris.util.mantle.Mantle;
|
||||
import com.volmit.iris.util.math.Vector3d;
|
||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.RandomSequences;
|
||||
import net.minecraft.world.attribute.EnvironmentAttributes;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.block.CraftBlockStates;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.util.CraftMagicNumbers;
|
||||
import org.bukkit.craftbukkit.v1_21_R7.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
private final BlockData AIR = Material.AIR.createBlockData();
|
||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
||||
private final AtomicBoolean injected = new AtomicBoolean();
|
||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||
private Field biomeStorageCache = null;
|
||||
|
||||
private static Object getFor(Class<?> type, Object source) {
|
||||
Object o = fieldFor(type, source);
|
||||
|
||||
if (o != null) {
|
||||
return o;
|
||||
}
|
||||
|
||||
return invokeFor(type, source);
|
||||
}
|
||||
|
||||
private static Object invokeFor(Class<?> returns, Object in) {
|
||||
for (Method i : in.getClass().getMethods()) {
|
||||
if (i.getReturnType().equals(returns)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
||||
return i.invoke(in);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object fieldFor(Class<?> returns, Object in) {
|
||||
return fieldForClass(returns, in.getClass(), in);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
||||
for (Field i : sourceType.getDeclaredFields()) {
|
||||
if (i.getType().equals(returnType)) {
|
||||
i.setAccessible(true);
|
||||
try {
|
||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
||||
return (T) i.get(in);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
||||
return type.getDeclaredClasses()[ordinal];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTile(Material material) {
|
||||
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTile(Location l) {
|
||||
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public KMap<String, Object> serializeTile(Location location) {
|
||||
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry());
|
||||
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
||||
}
|
||||
|
||||
@Contract(value = "null, _, _ -> null", pure = true)
|
||||
private Object convertFromTag(Tag tag, int depth, int maxDepth) {
|
||||
if (tag == null || depth > maxDepth) return null;
|
||||
return switch (tag) {
|
||||
case CollectionTag collection -> {
|
||||
KList<Object> list = new KList<>();
|
||||
|
||||
for (Object i : collection) {
|
||||
if (i instanceof Tag t)
|
||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
||||
else list.add(i);
|
||||
}
|
||||
yield list;
|
||||
}
|
||||
case net.minecraft.nbt.CompoundTag compound -> {
|
||||
KMap<String, Object> map = new KMap<>();
|
||||
|
||||
for (String key : compound.keySet()) {
|
||||
var child = compound.get(key);
|
||||
if (child == null) continue;
|
||||
var value = convertFromTag(child, depth + 1, maxDepth);
|
||||
if (value == null) continue;
|
||||
map.put(key, value);
|
||||
}
|
||||
yield map;
|
||||
}
|
||||
case NumericTag numeric -> numeric.box();
|
||||
default -> tag.asString().orElse(null);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
J.s(() -> merge(level, blockPos, tag));
|
||||
}
|
||||
|
||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
||||
var blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||
var state = level.getBlockState(blockPos);
|
||||
if (!state.hasBlockEntity())
|
||||
return;
|
||||
|
||||
blockEntity = ((EntityBlock) state.getBlock())
|
||||
.newBlockEntity(blockPos, state);
|
||||
}
|
||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||
accessor.setData(accessor.getData().merge(tag));
|
||||
}
|
||||
|
||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
||||
return switch (object) {
|
||||
case Map<?, ?> map -> {
|
||||
var tag = new net.minecraft.nbt.CompoundTag();
|
||||
for (var i : map.entrySet()) {
|
||||
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
||||
}
|
||||
yield tag;
|
||||
}
|
||||
case List<?> list -> {
|
||||
var tag = new ListTag();
|
||||
for (var i : list) {
|
||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
||||
}
|
||||
yield tag;
|
||||
}
|
||||
case Byte number -> ByteTag.valueOf(number);
|
||||
case Short number -> ShortTag.valueOf(number);
|
||||
case Integer number -> IntTag.valueOf(number);
|
||||
case Long number -> LongTag.valueOf(number);
|
||||
case Float number -> FloatTag.valueOf(number);
|
||||
case Double number -> DoubleTag.valueOf(number);
|
||||
case String string -> StringTag.valueOf(string);
|
||||
default -> EndTag.INSTANCE;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeEntity(Entity location) {
|
||||
return null;// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
||||
return null;// TODO:
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomHeight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private RegistryAccess registry() {
|
||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
||||
}
|
||||
|
||||
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
||||
return registry().lookup(Registries.BIOME).orElse(null);
|
||||
}
|
||||
|
||||
private Registry<Block> getBlockRegistry() {
|
||||
return registry().lookup(Registries.BLOCK).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBaseFromId(int id) {
|
||||
return getCustomBiomeRegistry().get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight(World world) {
|
||||
return world.getMinHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomBiomes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrueBiomeBaseId(Object biomeBase) {
|
||||
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTrueBiomeBase(Location location) {
|
||||
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTrueBiomeBaseKey(Location location) {
|
||||
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCustomBiomeBaseFor(String mckey) {
|
||||
return getCustomBiomeRegistry().getValue(net.minecraft.resources.Identifier.parse(mckey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
||||
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(mckey)))).orElse(null);
|
||||
}
|
||||
|
||||
public int getBiomeBaseIdForKey(String key) {
|
||||
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(key)).map(Holder::value).orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyForBiomeBase(Object biomeBase) {
|
||||
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBase(World world, Biome biome) {
|
||||
return biomeToBiomeBase(((CraftWorld) world).getHandle()
|
||||
.registryAccess().lookup(Registries.BIOME).orElse(null), biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBiomeBase(Object registry, Biome biome) {
|
||||
Object v = baseBiomeCache.get(biome);
|
||||
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
//noinspection unchecked
|
||||
v = biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
||||
if (v == null) {
|
||||
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
||||
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
||||
// So, we will just return the ID that the plains biome returns instead.
|
||||
//noinspection unchecked
|
||||
return biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
||||
}
|
||||
baseBiomeCache.put(biome, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<Biome> getBiomes() {
|
||||
return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBukkit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBiomeId(Biome biome) {
|
||||
for (World i : Bukkit.getWorlds()) {
|
||||
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
||||
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null);
|
||||
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
||||
}
|
||||
}
|
||||
|
||||
return biome.ordinal();
|
||||
}
|
||||
|
||||
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
||||
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
||||
return getCustomBiomeRegistry().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
||||
return getCustomBiomeRegistry().getId(paramT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
||||
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
||||
return new MCABiomeContainer() {
|
||||
@Override
|
||||
public int[] getData() {
|
||||
return base.writeBiomes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int y, int z, int id) {
|
||||
base.setBiome(x, y, z, biomeMapping.byId(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBiome(int x, int y, int z) {
|
||||
return biomeMapping.getId(base.getBiome(x, y, z));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countCustomBiomes() {
|
||||
AtomicInteger a = new AtomicInteger(0);
|
||||
|
||||
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
||||
if (i.getNamespace().equals("minecraft")) {
|
||||
return;
|
||||
}
|
||||
|
||||
a.incrementAndGet();
|
||||
Iris.debug("Custom Biome: " + i);
|
||||
});
|
||||
|
||||
return a.get();
|
||||
}
|
||||
|
||||
public boolean supportsDataPacks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
||||
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
||||
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
||||
c.markUnsaved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
||||
try {
|
||||
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
||||
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
||||
s.setBiome(x, y, z, biome);
|
||||
} catch (IllegalAccessException e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Field getFieldForBiomeStorage(Object storage) {
|
||||
Field f = biomeStorageCache;
|
||||
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
try {
|
||||
f = storage.getClass().getDeclaredField("biome");
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.error(storage.getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
biomeStorageCache = f;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCAPaletteAccess createPalette() {
|
||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
||||
Field cf = IdMapper.class.getDeclaredField("tToId");
|
||||
Field df = IdMapper.class.getDeclaredField("idToT");
|
||||
Field bf = IdMapper.class.getDeclaredField("nextId");
|
||||
cf.setAccessible(true);
|
||||
df.setAccessible(true);
|
||||
bf.setAccessible(true);
|
||||
IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
||||
int b = bf.getInt(blockData);
|
||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
||||
return new MCAIdMapper<BlockState>(c, d, b);
|
||||
});
|
||||
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
||||
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||
((CraftBlockData) AIR).getState());
|
||||
return new MCAWrappedPalettedContainer<>(container,
|
||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
||||
ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL);
|
||||
AtomicInteger c = new AtomicInteger();
|
||||
AtomicInteger r = new AtomicInteger();
|
||||
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
||||
if (b != null) {
|
||||
if (b.isCustom()) {
|
||||
chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get());
|
||||
c.getAndIncrement();
|
||||
} else {
|
||||
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
||||
r.getAndIncrement();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
||||
if (customNbt != null && !customNbt.isEmpty()) {
|
||||
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
||||
|
||||
try {
|
||||
net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString());
|
||||
tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag());
|
||||
s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||
} catch (CommandSyntaxException var5) {
|
||||
throw new IllegalArgumentException(var5);
|
||||
}
|
||||
|
||||
return CraftItemStack.asBukkitCopy(s);
|
||||
} else {
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
|
||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||
worldGenContextField.setAccessible(true);
|
||||
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||
if (dimensionType != null && !dimensionType.identifier().getNamespace().equals("iris"))
|
||||
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.identifier().toString());
|
||||
|
||||
var newContext = new WorldGenContext(
|
||||
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
|
||||
|
||||
worldGenContextField.set(chunkMap, newContext);
|
||||
}
|
||||
|
||||
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
||||
Field[] fields = EntityType.class.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
||||
try {
|
||||
EntityType entityType = (EntityType) field.get(null);
|
||||
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
||||
Vector<Float> v1 = new Vector<>();
|
||||
v1.add(entityType.getHeight());
|
||||
entityType.getDimensions();
|
||||
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
||||
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
||||
return box;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
Iris.error("Unable to get entity dimensions!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getBiomeColor(Location location, BiomeColor type) {
|
||||
ServerLevel reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||
var pos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
var holder = reader.getBiome(pos);
|
||||
var biome = holder.value();
|
||||
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
||||
|
||||
var attributes = reader.environmentAttributes();
|
||||
int rgba = switch (type) {
|
||||
case FOG -> attributes.getValue(EnvironmentAttributes.FOG_COLOR, pos);
|
||||
case WATER -> biome.getWaterColor();
|
||||
case WATER_FOG -> attributes.getValue(EnvironmentAttributes.WATER_FOG_COLOR, pos);
|
||||
case SKY -> attributes.getValue(EnvironmentAttributes.SKY_COLOR, pos);
|
||||
case FOLIAGE -> biome.getFoliageColor();
|
||||
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
||||
};
|
||||
if (rgba == 0) {
|
||||
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().foliageColorOverride().isEmpty())
|
||||
return null;
|
||||
if (BiomeColor.GRASS == type && biome.getSpecialEffects().grassColorOverride().isEmpty())
|
||||
return null;
|
||||
}
|
||||
return new Color(rgba, true);
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getType().equals(fieldType))
|
||||
return f;
|
||||
}
|
||||
throw new NoSuchFieldException(fieldType.getName());
|
||||
} catch (NoSuchFieldException var4) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass == null) {
|
||||
throw var4;
|
||||
} else {
|
||||
return getField(superClass, fieldType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
|
||||
return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V1_21_11;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnChunkCount(World world) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<String> getStructureKeys() {
|
||||
KList<String> keys = new KList<>();
|
||||
|
||||
var registry = registry().lookup(Registries.STRUCTURE).orElse(null);
|
||||
if (registry == null) return keys;
|
||||
registry.keySet().stream().map(Identifier::toString).forEach(keys::add);
|
||||
registry.getTags()
|
||||
.map(HolderSet.Named::key)
|
||||
.map(TagKey::location)
|
||||
.map(Identifier::toString)
|
||||
.map(s -> "#" + s)
|
||||
.forEach(keys::add);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean missingDimensionTypes(String... keys) {
|
||||
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||
return !Arrays.stream(keys)
|
||||
.map(key -> Identifier.fromNamespaceAndPath("iris", key))
|
||||
.allMatch(type::containsKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean injectBukkit() {
|
||||
if (injected.getAndSet(true))
|
||||
return true;
|
||||
try {
|
||||
Iris.info("Injecting Bukkit");
|
||||
var buddy = new ByteBuddy();
|
||||
buddy.redefine(ServerLevel.class)
|
||||
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(
|
||||
MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class,
|
||||
ResourceKey.class, LevelStem.class, boolean.class, long.class, List.class,
|
||||
boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class))))
|
||||
.make()
|
||||
.load(ServerLevel.class.getClassLoader(), Agent.installed());
|
||||
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
|
||||
buddy.redefine(clazz)
|
||||
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
|
||||
.make()
|
||||
.load(clazz.getClassLoader(), Agent.installed());
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
Iris.error(C.RED + "Failed to inject Bukkit");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMap<Material, List<BlockProperty>> getBlockProperties() {
|
||||
KMap<Material, List<BlockProperty>> states = new KMap<>();
|
||||
|
||||
for (var block : registry().lookupOrThrow(Registries.BLOCK)) {
|
||||
var state = block.defaultBlockState();
|
||||
if (state == null) state = block.getStateDefinition().any();
|
||||
final var finalState = state;
|
||||
|
||||
states.put(CraftMagicNumbers.getMaterial(block), block.getStateDefinition()
|
||||
.getProperties()
|
||||
.stream()
|
||||
.map(p -> createProperty(p, finalState))
|
||||
.toList());
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
private <T extends Comparable<T>> BlockProperty createProperty(Property<T> property, BlockState state) {
|
||||
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeStructures(Chunk chunk) {
|
||||
var craft = ((CraftChunk) chunk);
|
||||
var level = craft.getCraftWorld().getHandle();
|
||||
var access = ((CraftChunk) chunk).getHandle(ChunkStatus.FULL);
|
||||
level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMap<com.volmit.iris.core.link.Identifier, StructurePlacement> collectStructures() {
|
||||
var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET);
|
||||
var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT);
|
||||
return structureSets.keySet()
|
||||
.stream()
|
||||
.map(structureSets::get)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.map(holder -> {
|
||||
var set = holder.value();
|
||||
var placement = set.placement();
|
||||
var key = holder.key().identifier();
|
||||
StructurePlacement.StructurePlacementBuilder<?, ?> builder;
|
||||
if (placement instanceof RandomSpreadStructurePlacement random) {
|
||||
builder = StructurePlacement.RandomSpread.builder()
|
||||
.separation(random.separation())
|
||||
.spacing(random.spacing())
|
||||
.spreadType(switch (random.spreadType()) {
|
||||
case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR;
|
||||
case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR;
|
||||
});
|
||||
} else if (placement instanceof ConcentricRingsStructurePlacement rings) {
|
||||
builder = StructurePlacement.ConcentricRings.builder()
|
||||
.distance(rings.distance())
|
||||
.spread(rings.spread())
|
||||
.count(rings.count());
|
||||
} else {
|
||||
Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type()));
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Pair<>(new com.volmit.iris.core.link.Identifier(key.getNamespace(), key.getPath()), builder
|
||||
.salt(placement.salt)
|
||||
.frequency(placement.frequency)
|
||||
.structures(set.structures()
|
||||
.stream()
|
||||
.map(entry -> new StructurePlacement.Structure(
|
||||
entry.weight(),
|
||||
entry.structure()
|
||||
.unwrapKey()
|
||||
.map(ResourceKey::identifier)
|
||||
.map(Identifier::toString)
|
||||
.orElse(null),
|
||||
entry.structure().tags()
|
||||
.map(TagKey::location)
|
||||
.map(Identifier::toString)
|
||||
.toList()
|
||||
))
|
||||
.filter(StructurePlacement.Structure::isValid)
|
||||
.toList())
|
||||
.build());
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a, KMap::new));
|
||||
}
|
||||
|
||||
public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) {
|
||||
if (!(raw instanceof PlatformChunkGenerator gen))
|
||||
throw new IllegalStateException("Generator is not platform chunk generator!");
|
||||
|
||||
var dimensionKey = Identifier.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey());
|
||||
var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
|
||||
return new LevelStem(dimensionType, chunkGenerator(access));
|
||||
}
|
||||
|
||||
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
|
||||
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
|
||||
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
|
||||
settings.updateLayers();
|
||||
return new FlatLevelSource(settings);
|
||||
}
|
||||
|
||||
private static class ChunkAccessAdvice {
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
|
||||
return index >= access.getPostProcessing().length;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ServerLevelAdvice {
|
||||
@Advice.OnMethodEnter
|
||||
static void enter(
|
||||
@Advice.Argument(0) MinecraftServer server,
|
||||
@Advice.Argument(3) PrimaryLevelData levelData,
|
||||
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem,
|
||||
@Advice.Argument(11) World.Environment env,
|
||||
@Advice.Argument(12) ChunkGenerator gen
|
||||
) {
|
||||
if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris"))
|
||||
return;
|
||||
|
||||
try {
|
||||
Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
|
||||
.getClass()
|
||||
.getClassLoader())
|
||||
.getDeclaredMethod("get")
|
||||
.invoke(null);
|
||||
levelStem = (LevelStem) bindings.getClass()
|
||||
.getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class)
|
||||
.invoke(bindings, server.registryAccess(), gen);
|
||||
|
||||
levelData.customDimensions = null;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,15 @@ rootProject.name = "Iris"
|
||||
|
||||
include(":core", ":core:agent")
|
||||
include(
|
||||
":nms:v1_21_R6",
|
||||
":nms:v1_21_R5",
|
||||
":nms:v1_21_R4",
|
||||
":nms:v1_21_R3",
|
||||
":nms:v1_21_R2",
|
||||
":nms:v1_21_R1",
|
||||
":nms:v1_20_R4",
|
||||
":nms:v1_20_R3",
|
||||
":nms:v1_20_R2",
|
||||
":nms:v1_20_R1",
|
||||
":nms:v1_21_R7",
|
||||
":nms:v1_21_R6",
|
||||
":nms:v1_21_R5",
|
||||
":nms:v1_21_R4",
|
||||
":nms:v1_21_R3",
|
||||
":nms:v1_21_R2",
|
||||
":nms:v1_21_R1",
|
||||
":nms:v1_20_R4",
|
||||
":nms:v1_20_R3",
|
||||
":nms:v1_20_R2",
|
||||
":nms:v1_20_R1",
|
||||
)
|
||||
Reference in New Issue
Block a user