mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-05-20 00:20:24 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 635ee02459 | |||
| 2c7b7c8c91 | |||
| ad0662be54 | |||
| 835c8422f1 | |||
| 2c60192de3 | |||
| adb7188eb9 | |||
| 2436ebb857 | |||
| ad85a0bbd1 | |||
| 22ac9ebf47 | |||
| 3cb9585dd8 | |||
| f4d1177c51 | |||
| b132862a60 | |||
| 2452a2f633 | |||
| f0476fea9b | |||
| 90c6457d37 | |||
| 61301ffd4d | |||
| e42317139d | |||
| 97ddfd309b | |||
| 5d42c5cae0 | |||
| 52fecb48d8 | |||
| ce29dc98c3 | |||
| 463e3d9658 | |||
| 9152b25d51 | |||
| bb9c72e414 | |||
| 976648340e | |||
| 49ce84a9e9 | |||
| f93995e152 | |||
| 7a5a2e5909 | |||
| e15d31a0ed | |||
| 09cdd61a68 | |||
| 0575cd85c8 | |||
| f1c72974fd | |||
| 26e2e20840 | |||
| c00dcf205b | |||
| a10a784c3b | |||
| f99cc61042 | |||
| d2a1e5cc1e | |||
| 3e2c0fa025 | |||
| ab4770400e | |||
| 1a810d5d62 | |||
| 536d20bca7 | |||
| 9a691ac5b4 | |||
| 71078a20a9 | |||
| 566fca2b52 | |||
| 74e2576ca2 | |||
| fb0bc112e3 | |||
| 407e51378c | |||
| c468eb1ab1 | |||
| bdb7cc61e5 | |||
| e8f9e841c4 | |||
| 1b1b9d97b7 | |||
| 24355064ff | |||
| 06a45056d9 | |||
| dfe4894be7 | |||
| 8eb2287ec0 |
+34
-7
@@ -1,3 +1,5 @@
|
|||||||
|
import xyz.jpenilla.runpaper.task.RunServer
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
||||||
@@ -30,10 +32,11 @@ plugins {
|
|||||||
id 'java-library'
|
id 'java-library'
|
||||||
id "io.github.goooler.shadow" version "8.1.7"
|
id "io.github.goooler.shadow" version "8.1.7"
|
||||||
id "de.undercouch.download" version "5.0.1"
|
id "de.undercouch.download" version "5.0.1"
|
||||||
|
id "xyz.jpenilla.run-paper" version "2.3.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
version '3.6.3-1.20.1-1.21.4'
|
version '3.6.9-1.20.1-1.21.5'
|
||||||
|
|
||||||
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
||||||
// ======================== WINDOWS =============================
|
// ======================== WINDOWS =============================
|
||||||
@@ -53,7 +56,13 @@ registerCustomOutputTaskUnix('PixelMac', '/Users/test/Desktop/mcserver/plugins')
|
|||||||
registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins')
|
registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins')
|
||||||
// ==============================================================
|
// ==============================================================
|
||||||
|
|
||||||
|
def MIN_HEAP_SIZE = "2G"
|
||||||
|
def MAX_HEAP_SIZE = "8G"
|
||||||
|
//Valid values are: none, truecolor, indexed256, indexed16, indexed8
|
||||||
|
def COLOR = "truecolor"
|
||||||
|
|
||||||
def NMS_BINDINGS = Map.of(
|
def NMS_BINDINGS = Map.of(
|
||||||
|
"v1_21_R4", "1.21.5-R0.1-SNAPSHOT",
|
||||||
"v1_21_R3", "1.21.4-R0.1-SNAPSHOT",
|
"v1_21_R3", "1.21.4-R0.1-SNAPSHOT",
|
||||||
"v1_21_R2", "1.21.3-R0.1-SNAPSHOT",
|
"v1_21_R2", "1.21.3-R0.1-SNAPSHOT",
|
||||||
"v1_21_R1", "1.21.1-R0.1-SNAPSHOT",
|
"v1_21_R1", "1.21.1-R0.1-SNAPSHOT",
|
||||||
@@ -62,21 +71,34 @@ def NMS_BINDINGS = Map.of(
|
|||||||
"v1_20_R2", "1.20.2-R0.1-SNAPSHOT",
|
"v1_20_R2", "1.20.2-R0.1-SNAPSHOT",
|
||||||
"v1_20_R1", "1.20.1-R0.1-SNAPSHOT",
|
"v1_20_R1", "1.20.1-R0.1-SNAPSHOT",
|
||||||
)
|
)
|
||||||
def JVM_VERSION = Map.of()
|
def JVM_VERSION = Map.<String, Integer>of()
|
||||||
NMS_BINDINGS.each { nms ->
|
NMS_BINDINGS.forEach { key, value ->
|
||||||
project(":nms:${nms.key}") {
|
project(":nms:$key") {
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'com.volmit.nmstools'
|
apply plugin: 'com.volmit.nmstools'
|
||||||
|
|
||||||
nmsTools {
|
nmsTools {
|
||||||
it.jvm = JVM_VERSION.getOrDefault(nms.key, 21)
|
it.jvm = JVM_VERSION.getOrDefault(key, 21)
|
||||||
it.version = nms.value
|
it.version = value
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":core")
|
implementation project(":core")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register("runServer-$key", RunServer) {
|
||||||
|
group("servers")
|
||||||
|
minecraftVersion(value.split("-")[0])
|
||||||
|
minHeapSize(MIN_HEAP_SIZE)
|
||||||
|
maxHeapSize(MAX_HEAP_SIZE)
|
||||||
|
pluginJars(tasks.shadowJar.archiveFile)
|
||||||
|
javaLauncher = javaToolchains.launcherFor { it.languageVersion = JavaLanguageVersion.of(JVM_VERSION.getOrDefault(key, 21))}
|
||||||
|
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
|
||||||
|
systemProperty("disable.watchdog", "")
|
||||||
|
systemProperty("net.kyori.ansi.colorLevel", COLOR)
|
||||||
|
systemProperty("com.mojang.eula.agree", true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
@@ -92,6 +114,11 @@ shadowJar {
|
|||||||
relocate 'net.kyori', 'com.volmit.iris.util.kyori'
|
relocate 'net.kyori', 'com.volmit.iris.util.kyori'
|
||||||
relocate 'org.bstats', 'com.volmit.util.metrics'
|
relocate 'org.bstats', 'com.volmit.util.metrics'
|
||||||
archiveFileName.set("Iris-${project.version}.jar")
|
archiveFileName.set("Iris-${project.version}.jar")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
exclude(dependency("org.ow2.asm:asm:"))
|
||||||
|
exclude(dependency("org.jetbrains:"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -118,7 +145,7 @@ allprojects {
|
|||||||
maven { url "https://repo.triumphteam.dev/snapshots" }
|
maven { url "https://repo.triumphteam.dev/snapshots" }
|
||||||
maven { url "https://repo.mineinabyss.com/releases" }
|
maven { url "https://repo.mineinabyss.com/releases" }
|
||||||
maven { url 'https://hub.jeff-media.com/nexus/repository/jeff-media-public/' }
|
maven { url 'https://hub.jeff-media.com/nexus/repository/jeff-media-public/' }
|
||||||
maven { url "https://repo.nexomc.com/snapshots/" }
|
maven { url "https://repo.nexomc.com/releases/" }
|
||||||
maven { url "https://libraries.minecraft.net" }
|
maven { url "https://libraries.minecraft.net" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ dependencies {
|
|||||||
|
|
||||||
// Third Party Integrations
|
// Third Party Integrations
|
||||||
compileOnly 'com.ticxo.playeranimator:PlayerAnimator:R1.2.7'
|
compileOnly 'com.ticxo.playeranimator:PlayerAnimator:R1.2.7'
|
||||||
compileOnly 'com.nexomc:nexo:1.0.0-dev.38'
|
compileOnly 'com.nexomc:nexo:1.6.0'
|
||||||
compileOnly 'com.github.LoneDev6:api-itemsadder:3.4.1-r4'
|
compileOnly 'com.github.LoneDev6:api-itemsadder:3.4.1-r4'
|
||||||
compileOnly 'com.github.PlaceholderAPI:placeholderapi:2.11.3'
|
compileOnly 'com.github.PlaceholderAPI:placeholderapi:2.11.3'
|
||||||
compileOnly 'com.github.Ssomar-Developement:SCore:4.23.10.8'
|
compileOnly 'com.github.Ssomar-Developement:SCore:4.23.10.8'
|
||||||
|
|||||||
@@ -459,15 +459,17 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
|
initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class<? extends IrisService>) i.getClass(), (IrisService) i));
|
||||||
INMS.get();
|
INMS.get();
|
||||||
IO.delete(new File("iris"));
|
IO.delete(new File("iris"));
|
||||||
|
compat = IrisCompat.configured(getDataFile("compat.json"));
|
||||||
|
ServerConfigurator.configure();
|
||||||
|
new IrisContextInjector();
|
||||||
IrisSafeguard.IrisSafeguardSystem();
|
IrisSafeguard.IrisSafeguardSystem();
|
||||||
getSender().setTag(getTag());
|
getSender().setTag(getTag());
|
||||||
compat = IrisCompat.configured(getDataFile("compat.json"));
|
IrisSafeguard.earlySplash();
|
||||||
linkMultiverseCore = new MultiverseCoreLink();
|
linkMultiverseCore = new MultiverseCoreLink();
|
||||||
linkMythicMobs = new MythicMobsLink();
|
linkMythicMobs = new MythicMobsLink();
|
||||||
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
||||||
services.values().forEach(IrisService::onEnable);
|
services.values().forEach(IrisService::onEnable);
|
||||||
services.values().forEach(this::registerListener);
|
services.values().forEach(this::registerListener);
|
||||||
registerListener(new IrisContextInjector());
|
|
||||||
J.s(() -> {
|
J.s(() -> {
|
||||||
J.a(() -> PaperLib.suggestPaper(this));
|
J.a(() -> PaperLib.suggestPaper(this));
|
||||||
J.a(() -> IO.delete(getTemp()));
|
J.a(() -> IO.delete(getTemp()));
|
||||||
@@ -574,9 +576,19 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
Bukkit.getScheduler().cancelTasks(this);
|
Bukkit.getScheduler().cancelTasks(this);
|
||||||
HandlerList.unregisterAll((Plugin) this);
|
HandlerList.unregisterAll((Plugin) this);
|
||||||
postShutdown.forEach(Runnable::run);
|
postShutdown.forEach(Runnable::run);
|
||||||
services.clear();
|
|
||||||
MultiBurst.burst.close();
|
|
||||||
super.onDisable();
|
super.onDisable();
|
||||||
|
|
||||||
|
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan);
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
Bukkit.getWorlds()
|
||||||
|
.stream()
|
||||||
|
.map(IrisToolbelt::access)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(PlatformChunkGenerator::close);
|
||||||
|
|
||||||
|
MultiBurst.burst.close();
|
||||||
|
services.clear();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPapi() {
|
private void setupPapi() {
|
||||||
@@ -622,12 +634,22 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
Iris.warn("=");
|
Iris.warn("=");
|
||||||
Iris.warn("============================================");
|
Iris.warn("============================================");
|
||||||
}
|
}
|
||||||
if (!instance.getServer().getVersion().contains("Purpur")) {
|
|
||||||
passed = false;
|
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("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("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("Plugins that work on Spigot / Paper work on Purpur.");
|
||||||
Iris.info("You can download it here: https://purpurmc.org");
|
Iris.info("You can download it here: https://purpurmc.org");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return passed;
|
return passed;
|
||||||
}
|
}
|
||||||
@@ -852,13 +874,6 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
|
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
|
||||||
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
|
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
|
||||||
Iris.info("Java: " + getJava());
|
Iris.info("Java: " + getJava());
|
||||||
if (!instance.getServer().getVersion().contains("Purpur")) {
|
|
||||||
if (instance.getServer().getVersion().contains("Spigot") && instance.getServer().getVersion().contains("Bukkit")) {
|
|
||||||
Iris.info(C.RED + " Iris requires paper or above to function properly..");
|
|
||||||
} else {
|
|
||||||
Iris.info(C.YELLOW + "Purpur is recommended to use with iris.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (getHardware.getProcessMemory() < 5999) {
|
if (getHardware.getProcessMemory() < 5999) {
|
||||||
Iris.warn("6GB+ Ram is recommended");
|
Iris.warn("6GB+ Ram is recommended");
|
||||||
Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB");
|
Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB");
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import com.volmit.iris.Iris;
|
|||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
import com.volmit.iris.util.json.JSONException;
|
import com.volmit.iris.util.json.JSONException;
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
import com.volmit.iris.util.json.JSONObject;
|
||||||
|
import com.volmit.iris.util.misc.getHardware;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -45,6 +45,7 @@ public class IrisSettings {
|
|||||||
private IrisSettingsStudio studio = new IrisSettingsStudio();
|
private IrisSettingsStudio studio = new IrisSettingsStudio();
|
||||||
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
|
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
|
||||||
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
|
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
|
||||||
|
private IrisSettingsPregen pregen = new IrisSettingsPregen();
|
||||||
|
|
||||||
public static int getThreadCount(int c) {
|
public static int getThreadCount(int c) {
|
||||||
return switch (c) {
|
return switch (c) {
|
||||||
@@ -130,21 +131,46 @@ public class IrisSettings {
|
|||||||
public boolean markerEntitySpawningSystem = true;
|
public boolean markerEntitySpawningSystem = true;
|
||||||
public boolean effectSystem = true;
|
public boolean effectSystem = true;
|
||||||
public boolean worldEditWandCUI = true;
|
public boolean worldEditWandCUI = true;
|
||||||
|
public boolean globalPregenCache = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class IrisSettingsConcurrency {
|
public static class IrisSettingsConcurrency {
|
||||||
public int parallelism = -1;
|
public int parallelism = -1;
|
||||||
|
public int worldGenParallelism = -1;
|
||||||
|
|
||||||
|
public int getWorldGenThreads() {
|
||||||
|
return getThreadCount(worldGenParallelism);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class IrisSettingsPregen {
|
||||||
|
public boolean useCacheByDefault = true;
|
||||||
|
public boolean useHighPriority = false;
|
||||||
|
public boolean useVirtualThreads = false;
|
||||||
|
public boolean useTicketQueue = false;
|
||||||
|
public int maxConcurrency = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class IrisSettingsPerformance {
|
public static class IrisSettingsPerformance {
|
||||||
|
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
|
||||||
public boolean trimMantleInStudio = false;
|
public boolean trimMantleInStudio = false;
|
||||||
public int mantleKeepAlive = 30;
|
public int mantleKeepAlive = 30;
|
||||||
public int cacheSize = 4_096;
|
public int cacheSize = 4_096;
|
||||||
public int resourceLoaderCacheSize = 1_024;
|
public int resourceLoaderCacheSize = 1_024;
|
||||||
public int objectLoaderCacheSize = 4_096;
|
public int objectLoaderCacheSize = 4_096;
|
||||||
public int scriptLoaderCacheSize = 512;
|
public int scriptLoaderCacheSize = 512;
|
||||||
|
public int tectonicPlateSize = -1;
|
||||||
|
public int mantleCleanupDelay = 200;
|
||||||
|
|
||||||
|
public int getTectonicPlateSize() {
|
||||||
|
if (tectonicPlateSize > 0)
|
||||||
|
return tectonicPlateSize;
|
||||||
|
|
||||||
|
return (int) (getHardware.getProcessMemory() / 200L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -176,6 +202,7 @@ public class IrisSettings {
|
|||||||
public boolean DoomsdayAnnihilationSelfDestructMode = false;
|
public boolean DoomsdayAnnihilationSelfDestructMode = false;
|
||||||
public boolean commandSounds = true;
|
public boolean commandSounds = true;
|
||||||
public boolean debug = false;
|
public boolean debug = false;
|
||||||
|
public boolean dumpMantleOnError = false;
|
||||||
public boolean disableNMS = false;
|
public boolean disableNMS = false;
|
||||||
public boolean pluginMetrics = true;
|
public boolean pluginMetrics = true;
|
||||||
public boolean splashLogoStartup = true;
|
public boolean splashLogoStartup = true;
|
||||||
@@ -216,4 +243,14 @@ public class IrisSettings {
|
|||||||
public boolean disableTimeAndWeather = true;
|
public boolean disableTimeAndWeather = true;
|
||||||
public boolean autoStartDefaultStudio = false;
|
public boolean autoStartDefaultStudio = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class IrisSettingsEngineSVC {
|
||||||
|
public boolean useVirtualThreads = true;
|
||||||
|
public int priority = Thread.NORM_PRIORITY;
|
||||||
|
|
||||||
|
public int getPriority() {
|
||||||
|
return Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import com.volmit.iris.util.collection.KList;
|
|||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.misc.ServerProperties;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -70,10 +71,12 @@ public class ServerConfigurator {
|
|||||||
f.load(spigotConfig);
|
f.load(spigotConfig);
|
||||||
long tt = f.getLong("settings.timeout-time");
|
long tt = f.getLong("settings.timeout-time");
|
||||||
|
|
||||||
if (tt < TimeUnit.MINUTES.toSeconds(5)) {
|
long spigotTimeout = TimeUnit.MINUTES.toSeconds(5);
|
||||||
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + TimeUnit.MINUTES.toSeconds(20) + " (5 minutes)");
|
|
||||||
|
if (tt < spigotTimeout) {
|
||||||
|
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + spigotTimeout + " (5 minutes)");
|
||||||
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
||||||
f.set("settings.timeout-time", TimeUnit.MINUTES.toSeconds(5));
|
f.set("settings.timeout-time", spigotTimeout);
|
||||||
f.save(spigotConfig);
|
f.save(spigotConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,10 +86,11 @@ public class ServerConfigurator {
|
|||||||
f.load(spigotConfig);
|
f.load(spigotConfig);
|
||||||
long tt = f.getLong("watchdog.early-warning-delay");
|
long tt = f.getLong("watchdog.early-warning-delay");
|
||||||
|
|
||||||
if (tt < TimeUnit.MINUTES.toMillis(3)) {
|
long watchdog = TimeUnit.MINUTES.toMillis(3);
|
||||||
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + TimeUnit.MINUTES.toMillis(15) + " (3 minutes)");
|
if (tt < watchdog) {
|
||||||
|
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + watchdog + " (3 minutes)");
|
||||||
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
||||||
f.set("watchdog.early-warning-delay", TimeUnit.MINUTES.toMillis(3));
|
f.set("watchdog.early-warning-delay", watchdog);
|
||||||
f.save(spigotConfig);
|
f.save(spigotConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,6 +101,7 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
KList<File> worlds = new KList<>();
|
KList<File> worlds = new KList<>();
|
||||||
Bukkit.getServer().getWorlds().forEach(w -> worlds.add(new File(w.getWorldFolder(), "datapacks")));
|
Bukkit.getServer().getWorlds().forEach(w -> worlds.add(new File(w.getWorldFolder(), "datapacks")));
|
||||||
|
if (worlds.isEmpty()) worlds.add(new File(Bukkit.getWorldContainer(), ServerProperties.LEVEL_NAME + "/datapacks"));
|
||||||
return worlds;
|
return worlds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.core.commands;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.pregenerator.DeepSearchPregenerator;
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenTask;
|
|
||||||
import com.volmit.iris.core.pregenerator.TurboPregenerator;
|
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
|
||||||
import com.volmit.iris.util.data.Dimension;
|
|
||||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
|
||||||
import com.volmit.iris.util.decree.annotations.Decree;
|
|
||||||
import com.volmit.iris.util.decree.annotations.Param;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import com.volmit.iris.util.math.Position2;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Decree(name = "DeepSearch", aliases = "search", description = "Pregenerate your Iris worlds!")
|
|
||||||
public class CommandDeepSearch implements DecreeExecutor {
|
|
||||||
public String worldName;
|
|
||||||
@Decree(description = "DeepSearch a world")
|
|
||||||
public void start(
|
|
||||||
@Param(description = "The radius of the pregen in blocks", aliases = "size")
|
|
||||||
int radius,
|
|
||||||
@Param(description = "The world to pregen", contextual = true)
|
|
||||||
World world,
|
|
||||||
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
|
|
||||||
Vector center
|
|
||||||
) {
|
|
||||||
|
|
||||||
worldName = world.getName();
|
|
||||||
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
|
|
||||||
File TurboFile = new File(worldDirectory, "DeepSearch.json");
|
|
||||||
if (TurboFile.exists()) {
|
|
||||||
if (DeepSearchPregenerator.getInstance() != null) {
|
|
||||||
sender().sendMessage(C.BLUE + "DeepSearch is already in progress");
|
|
||||||
Iris.info(C.YELLOW + "DeepSearch is already in progress");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
TurboFile.delete();
|
|
||||||
} catch (Exception e){
|
|
||||||
Iris.error("Failed to delete the old instance file of DeepSearch!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (sender().isPlayer() && access() == null) {
|
|
||||||
sender().sendMessage(C.RED + "The engine access for this world is null!");
|
|
||||||
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
|
|
||||||
}
|
|
||||||
|
|
||||||
DeepSearchPregenerator.DeepSearchJob DeepSearchJob = DeepSearchPregenerator.DeepSearchJob.builder()
|
|
||||||
.world(world)
|
|
||||||
.radiusBlocks(radius)
|
|
||||||
.position(0)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
File SearchGenFile = new File(worldDirectory, "DeepSearch.json");
|
|
||||||
DeepSearchPregenerator pregenerator = new DeepSearchPregenerator(DeepSearchJob, SearchGenFile);
|
|
||||||
pregenerator.start();
|
|
||||||
|
|
||||||
String msg = C.GREEN + "DeepSearch started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
|
|
||||||
sender().sendMessage(msg);
|
|
||||||
Iris.info(msg);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
sender().sendMessage(C.RED + "Epic fail. See console.");
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decree(description = "Stop the active DeepSearch task", aliases = "x")
|
|
||||||
public void stop(@Param(aliases = "world", description = "The world to pause") World world) throws IOException {
|
|
||||||
DeepSearchPregenerator DeepSearchInstance = DeepSearchPregenerator.getInstance();
|
|
||||||
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
|
|
||||||
File turboFile = new File(worldDirectory, "DeepSearch.json");
|
|
||||||
|
|
||||||
if (DeepSearchInstance != null) {
|
|
||||||
DeepSearchInstance.shutdownInstance(world);
|
|
||||||
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
|
|
||||||
} else if (turboFile.exists() && turboFile.delete()) {
|
|
||||||
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
|
|
||||||
} else if (turboFile.exists()) {
|
|
||||||
Iris.error("Failed to delete the old instance file of Turbo Pregen!");
|
|
||||||
} else {
|
|
||||||
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"})
|
|
||||||
public void pause(
|
|
||||||
@Param(aliases = "world", description = "The world to pause")
|
|
||||||
World world
|
|
||||||
) {
|
|
||||||
if (TurboPregenerator.getInstance() != null) {
|
|
||||||
TurboPregenerator.setPausedTurbo(world);
|
|
||||||
sender().sendMessage(C.GREEN + "Paused/unpaused Turbo Pregen, now: " + (TurboPregenerator.isPausedTurbo(world) ? "Paused" : "Running") + ".");
|
|
||||||
} else {
|
|
||||||
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
|
|
||||||
File TurboFile = new File(worldDirectory, "DeepSearch.json");
|
|
||||||
if (TurboFile.exists()){
|
|
||||||
TurboPregenerator.loadTurboGenerator(world.getName());
|
|
||||||
sender().sendMessage(C.YELLOW + "Started DeepSearch back up!");
|
|
||||||
} else {
|
|
||||||
sender().sendMessage(C.YELLOW + "No active DeepSearch tasks to pause/unpause.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,6 @@ package com.volmit.iris.core.commands;
|
|||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.ServerConfigurator;
|
import com.volmit.iris.core.ServerConfigurator;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||||
@@ -36,6 +35,7 @@ import com.volmit.iris.util.format.Form;
|
|||||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||||
|
import com.volmit.iris.util.math.M;
|
||||||
import com.volmit.iris.util.nbt.mca.MCAFile;
|
import com.volmit.iris.util.nbt.mca.MCAFile;
|
||||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
@@ -62,57 +62,49 @@ import java.util.zip.GZIPOutputStream;
|
|||||||
@Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"})
|
@Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"})
|
||||||
public class CommandDeveloper implements DecreeExecutor {
|
public class CommandDeveloper implements DecreeExecutor {
|
||||||
private CommandTurboPregen turboPregen;
|
private CommandTurboPregen turboPregen;
|
||||||
|
private CommandLazyPregen lazyPregen;
|
||||||
private CommandUpdater updater;
|
private CommandUpdater updater;
|
||||||
|
|
||||||
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
||||||
public void EngineStatus() {
|
public void EngineStatus() {
|
||||||
List<World> IrisWorlds = new ArrayList<>();
|
Iris.service(IrisEngineSVC.class)
|
||||||
int TotalLoadedChunks = 0;
|
.engineStatus(sender());
|
||||||
int TotalQueuedTectonicPlates = 0;
|
}
|
||||||
int TotalNotQueuedTectonicPlates = 0;
|
|
||||||
int TotalTectonicPlates = 0;
|
|
||||||
|
|
||||||
long lowestUnloadDuration = 0;
|
@Decree(description = "Test")
|
||||||
long highestUnloadDuration = 0;
|
public void dumpThreads() {
|
||||||
|
try {
|
||||||
|
File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt");
|
||||||
|
FileOutputStream fos = new FileOutputStream(fi);
|
||||||
|
Map<Thread, StackTraceElement[]> f = Thread.getAllStackTraces();
|
||||||
|
PrintWriter pw = new PrintWriter(fos);
|
||||||
|
|
||||||
for (World world : Bukkit.getWorlds()) {
|
pw.println(Thread.activeCount() + "/" + f.size());
|
||||||
try {
|
var run = Runtime.getRuntime();
|
||||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
pw.println("Memory:");
|
||||||
IrisWorlds.add(world);
|
pw.println("\tMax: " + run.maxMemory());
|
||||||
|
pw.println("\tTotal: " + run.totalMemory());
|
||||||
|
pw.println("\tFree: " + run.freeMemory());
|
||||||
|
pw.println("\tUsed: " + (run.totalMemory() - run.freeMemory()));
|
||||||
|
|
||||||
|
for (Thread i : f.keySet()) {
|
||||||
|
pw.println("========================================");
|
||||||
|
pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name());
|
||||||
|
|
||||||
|
for (StackTraceElement j : f.get(i)) {
|
||||||
|
pw.println(" @ " + j.toString());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
// no
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (World world : IrisWorlds) {
|
pw.println("========================================");
|
||||||
Engine engine = IrisToolbelt.access(world).getEngine();
|
pw.println();
|
||||||
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
|
pw.println();
|
||||||
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
|
|
||||||
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
|
|
||||||
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
|
|
||||||
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
|
||||||
}
|
|
||||||
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
|
|
||||||
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
|
||||||
}
|
|
||||||
for (Chunk chunk : world.getLoadedChunks()) {
|
|
||||||
if (chunk.isLoaded()) {
|
|
||||||
TotalLoadedChunks++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pw.close();
|
||||||
|
Iris.info("DUMPED! See " + fi.getAbsolutePath());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
Iris.info("-------------------------");
|
|
||||||
Iris.info(C.DARK_PURPLE + "Engine Status");
|
|
||||||
Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit());
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration));
|
|
||||||
Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration));
|
|
||||||
Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
|
||||||
Iris.info("-------------------------");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
@@ -128,7 +120,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
|
|
||||||
File tectonicplates = new File(folder, "mantle");
|
File tectonicplates = new File(folder, "mantle");
|
||||||
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
|
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
|
||||||
TectonicPlate.read(maxHeight, i);
|
TectonicPlate.read(maxHeight, i, true);
|
||||||
c++;
|
c++;
|
||||||
Iris.info("Loaded count: " + c );
|
Iris.info("Loaded count: " + c );
|
||||||
|
|
||||||
@@ -140,7 +132,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
public void packBenchmark(
|
public void packBenchmark(
|
||||||
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
|
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
|
||||||
IrisDimension dimension,
|
IrisDimension dimension,
|
||||||
@Param(description = "Radius in regions", defaultValue = "5")
|
@Param(description = "Radius in regions", defaultValue = "2048")
|
||||||
int radius,
|
int radius,
|
||||||
@Param(description = "Open GUI while benchmarking", defaultValue = "false")
|
@Param(description = "Open GUI while benchmarking", defaultValue = "false")
|
||||||
boolean gui
|
boolean gui
|
||||||
@@ -234,7 +226,8 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
@Param(description = "base IrisWorld") World world,
|
@Param(description = "base IrisWorld") World world,
|
||||||
@Param(description = "raw TectonicPlate File") String path,
|
@Param(description = "raw TectonicPlate File") String path,
|
||||||
@Param(description = "Algorithm to Test") String algorithm,
|
@Param(description = "Algorithm to Test") String algorithm,
|
||||||
@Param(description = "Amount of Tests") int amount) {
|
@Param(description = "Amount of Tests") int amount,
|
||||||
|
@Param(description = "Is versioned", defaultValue = "false") boolean versioned) {
|
||||||
if (!IrisToolbelt.isIrisWorld(world)) {
|
if (!IrisToolbelt.isIrisWorld(world)) {
|
||||||
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
|
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
|
||||||
return;
|
return;
|
||||||
@@ -251,7 +244,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
service.submit(() -> {
|
service.submit(() -> {
|
||||||
try {
|
try {
|
||||||
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
|
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
|
||||||
TectonicPlate plate = new TectonicPlate(height, raw);
|
TectonicPlate plate = new TectonicPlate(height, raw, versioned);
|
||||||
raw.close();
|
raw.close();
|
||||||
|
|
||||||
double d1 = 0;
|
double d1 = 0;
|
||||||
@@ -270,7 +263,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
size = tmp.length();
|
size = tmp.length();
|
||||||
start = System.currentTimeMillis();
|
start = System.currentTimeMillis();
|
||||||
CountingDataInputStream din = createInput(tmp, algorithm);
|
CountingDataInputStream din = createInput(tmp, algorithm);
|
||||||
new TectonicPlate(height, din);
|
new TectonicPlate(height, din, true);
|
||||||
din.close();
|
din.close();
|
||||||
d2 += System.currentTimeMillis() - start;
|
d2 += System.currentTimeMillis() - start;
|
||||||
tmp.delete();
|
tmp.delete();
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.volmit.iris.core.loader.IrisData;
|
|||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
||||||
import com.volmit.iris.core.service.StudioSVC;
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
import com.volmit.iris.core.tools.IrisBenchmarking;
|
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
@@ -59,7 +58,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import static com.volmit.iris.Iris.service;
|
import static com.volmit.iris.Iris.service;
|
||||||
import static com.volmit.iris.core.service.EditSVC.deletingWorld;
|
import static com.volmit.iris.core.service.EditSVC.deletingWorld;
|
||||||
import static com.volmit.iris.core.tools.IrisBenchmarking.inProgress;
|
|
||||||
import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode;
|
import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode;
|
||||||
import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities;
|
import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities;
|
||||||
import static org.bukkit.Bukkit.getServer;
|
import static org.bukkit.Bukkit.getServer;
|
||||||
@@ -68,14 +66,12 @@ import static org.bukkit.Bukkit.getServer;
|
|||||||
public class CommandIris implements DecreeExecutor {
|
public class CommandIris implements DecreeExecutor {
|
||||||
private CommandStudio studio;
|
private CommandStudio studio;
|
||||||
private CommandPregen pregen;
|
private CommandPregen pregen;
|
||||||
private CommandLazyPregen lazyPregen;
|
|
||||||
private CommandSettings settings;
|
private CommandSettings settings;
|
||||||
private CommandObject object;
|
private CommandObject object;
|
||||||
private CommandJigsaw jigsaw;
|
private CommandJigsaw jigsaw;
|
||||||
private CommandWhat what;
|
private CommandWhat what;
|
||||||
private CommandEdit edit;
|
private CommandEdit edit;
|
||||||
private CommandFind find;
|
private CommandFind find;
|
||||||
private CommandSupport support;
|
|
||||||
private CommandDeveloper developer;
|
private CommandDeveloper developer;
|
||||||
public static boolean worldCreation = false;
|
public static boolean worldCreation = false;
|
||||||
String WorldEngine;
|
String WorldEngine;
|
||||||
@@ -175,16 +171,6 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software");
|
sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software");
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo Move to React
|
|
||||||
@Decree(description = "Benchmark your server", origin = DecreeOrigin.CONSOLE)
|
|
||||||
public void serverbenchmark() throws InterruptedException {
|
|
||||||
if(!inProgress) {
|
|
||||||
IrisBenchmarking.runBenchmark();
|
|
||||||
} else {
|
|
||||||
Iris.info(C.RED + "Benchmark already is in progress.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
/todo
|
/todo
|
||||||
@Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE)
|
@Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE)
|
||||||
|
|||||||
@@ -19,9 +19,7 @@
|
|||||||
package com.volmit.iris.core.commands;
|
package com.volmit.iris.core.commands;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.IrisSettings;
|
|
||||||
import com.volmit.iris.core.gui.PregeneratorJob;
|
import com.volmit.iris.core.gui.PregeneratorJob;
|
||||||
import com.volmit.iris.core.pregenerator.LazyPregenerator;
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenTask;
|
import com.volmit.iris.core.pregenerator.PregenTask;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||||
@@ -29,12 +27,9 @@ import com.volmit.iris.util.decree.annotations.Decree;
|
|||||||
import com.volmit.iris.util.decree.annotations.Param;
|
import com.volmit.iris.util.decree.annotations.Param;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.math.Position2;
|
import com.volmit.iris.util.math.Position2;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
@Decree(name = "pregen", aliases = "pregenerate", description = "Pregenerate your Iris worlds!")
|
@Decree(name = "pregen", aliases = "pregenerate", description = "Pregenerate your Iris worlds!")
|
||||||
public class CommandPregen implements DecreeExecutor {
|
public class CommandPregen implements DecreeExecutor {
|
||||||
@Decree(description = "Pregenerate a world")
|
@Decree(description = "Pregenerate a world")
|
||||||
@@ -44,7 +39,9 @@ public class CommandPregen implements DecreeExecutor {
|
|||||||
@Param(description = "The world to pregen", contextual = true)
|
@Param(description = "The world to pregen", contextual = true)
|
||||||
World world,
|
World world,
|
||||||
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
|
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
|
||||||
Vector center
|
Vector center,
|
||||||
|
@Param(description = "Open the Iris pregen gui", defaultValue = "true")
|
||||||
|
boolean gui
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (sender().isPlayer() && access() == null) {
|
if (sender().isPlayer() && access() == null) {
|
||||||
@@ -52,13 +49,12 @@ public class CommandPregen implements DecreeExecutor {
|
|||||||
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
|
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
|
||||||
}
|
}
|
||||||
radius = Math.max(radius, 1024);
|
radius = Math.max(radius, 1024);
|
||||||
int w = (radius >> 9 + 1) * 2;
|
|
||||||
IrisToolbelt.pregenerate(PregenTask
|
IrisToolbelt.pregenerate(PregenTask
|
||||||
.builder()
|
.builder()
|
||||||
.center(new Position2(center.getBlockX() >> 9, center.getBlockZ() >> 9))
|
.center(new Position2(center.getBlockX(), center.getBlockZ()))
|
||||||
.gui(true)
|
.gui(gui)
|
||||||
.width(w)
|
.radiusX(radius)
|
||||||
.height(w)
|
.radiusZ(radius)
|
||||||
.build(), world);
|
.build(), world);
|
||||||
String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
|
String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
|
||||||
sender().sendMessage(msg);
|
sender().sendMessage(msg);
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.core.commands;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
|
||||||
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
|
||||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
|
||||||
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.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
|
||||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
|
||||||
import com.volmit.iris.util.decree.annotations.Decree;
|
|
||||||
import com.volmit.iris.util.decree.annotations.Param;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import com.volmit.iris.util.io.IO;
|
|
||||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
|
||||||
import com.volmit.iris.util.misc.Hastebin;
|
|
||||||
import com.volmit.iris.util.misc.Platform;
|
|
||||||
import com.volmit.iris.util.misc.getHardware;
|
|
||||||
import com.volmit.iris.util.nbt.mca.MCAFile;
|
|
||||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
|
||||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
|
||||||
import org.apache.commons.lang.RandomStringUtils;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import oshi.SystemInfo;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
@Decree(name = "Support", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"support"})
|
|
||||||
public class CommandSupport implements DecreeExecutor {
|
|
||||||
|
|
||||||
@Decree(description = "report")
|
|
||||||
public void report() {
|
|
||||||
try {
|
|
||||||
if (sender().isPlayer()) sender().sendMessage(C.GOLD + "Creating report..");
|
|
||||||
if (!sender().isPlayer()) Iris.info(C.GOLD + "Creating report..");
|
|
||||||
Hastebin.enviornment(sender());
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.info(C.RED + "Something went wrong: ");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +24,6 @@ import com.volmit.iris.core.pregenerator.IrisPregenerator;
|
|||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
import com.volmit.iris.core.pregenerator.PregenTask;
|
import com.volmit.iris.core.pregenerator.PregenTask;
|
||||||
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
@@ -41,12 +40,13 @@ import java.awt.*;
|
|||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.KeyListener;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmarkInProgress;
|
|
||||||
|
|
||||||
public class PregeneratorJob implements PregenListener {
|
public class PregeneratorJob implements PregenListener {
|
||||||
private static final Color COLOR_EXISTS = parseColor("#4d7d5b");
|
private static final Color COLOR_EXISTS = parseColor("#4d7d5b");
|
||||||
private static final Color COLOR_BLACK = parseColor("#4d7d5b");
|
private static final Color COLOR_BLACK = parseColor("#4d7d5b");
|
||||||
@@ -56,7 +56,7 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c");
|
private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c");
|
||||||
private static final Color COLOR_GENERATED = parseColor("#65c295");
|
private static final Color COLOR_GENERATED = parseColor("#65c295");
|
||||||
private static final Color COLOR_CLEANED = parseColor("#34eb93");
|
private static final Color COLOR_CLEANED = parseColor("#34eb93");
|
||||||
public static PregeneratorJob instance;
|
private static final AtomicReference<PregeneratorJob> instance = new AtomicReference<>();
|
||||||
private final MemoryMonitor monitor;
|
private final MemoryMonitor monitor;
|
||||||
private final PregenTask task;
|
private final PregenTask task;
|
||||||
private final boolean saving;
|
private final boolean saving;
|
||||||
@@ -67,26 +67,33 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
private final Position2 max;
|
private final Position2 max;
|
||||||
private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1));
|
private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1));
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
|
private final ExecutorService service;
|
||||||
private JFrame frame;
|
private JFrame frame;
|
||||||
private PregenRenderer renderer;
|
private PregenRenderer renderer;
|
||||||
private int rgc = 0;
|
private int rgc = 0;
|
||||||
private String[] info;
|
private String[] info;
|
||||||
|
|
||||||
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
|
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
|
||||||
|
instance.updateAndGet(old -> {
|
||||||
|
if (old != null) {
|
||||||
|
old.pregenerator.close();
|
||||||
|
old.close();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
});
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
instance = this;
|
|
||||||
monitor = new MemoryMonitor(50);
|
monitor = new MemoryMonitor(50);
|
||||||
saving = false;
|
saving = false;
|
||||||
info = new String[]{"Initializing..."};
|
info = new String[]{"Initializing..."};
|
||||||
this.task = task;
|
this.task = task;
|
||||||
this.pregenerator = new IrisPregenerator(task, method, this);
|
this.pregenerator = new IrisPregenerator(task, method, this);
|
||||||
max = new Position2(0, 0);
|
max = new Position2(0, 0);
|
||||||
min = new Position2(0, 0);
|
min = new Position2(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||||
task.iterateRegions((xx, zz) -> {
|
task.iterateAllChunks((xx, zz) -> {
|
||||||
min.setX(Math.min(xx << 5, min.getX()));
|
min.setX(Math.min(xx, min.getX()));
|
||||||
min.setZ(Math.min(zz << 5, min.getZ()));
|
min.setZ(Math.min(zz, min.getZ()));
|
||||||
max.setX(Math.max((xx << 5) + 31, max.getX()));
|
max.setX(Math.max(xx, max.getX()));
|
||||||
max.setZ(Math.max((zz << 5) + 31, max.getZ()));
|
max.setZ(Math.max(zz, max.getZ()));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (IrisSettings.get().getGui().isUseServerLaunchedGuis() && task.isGui()) {
|
if (IrisSettings.get().getGui().isUseServerLaunchedGuis() && task.isGui()) {
|
||||||
@@ -99,40 +106,44 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
}, "Iris Pregenerator");
|
}, "Iris Pregenerator");
|
||||||
t.setPriority(Thread.MIN_PRIORITY);
|
t.setPriority(Thread.MIN_PRIORITY);
|
||||||
t.start();
|
t.start();
|
||||||
|
service = Executors.newVirtualThreadPerTaskExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean shutdownInstance() {
|
public static boolean shutdownInstance() {
|
||||||
if (instance == null) {
|
PregeneratorJob inst = instance.get();
|
||||||
|
if (inst == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
J.a(() -> instance.pregenerator.close());
|
J.a(inst.pregenerator::close);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PregeneratorJob getInstance() {
|
public static PregeneratorJob getInstance() {
|
||||||
return instance;
|
return instance.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean pauseResume() {
|
public static boolean pauseResume() {
|
||||||
if (instance == null) {
|
PregeneratorJob inst = instance.get();
|
||||||
|
if (inst == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPaused()) {
|
if (isPaused()) {
|
||||||
instance.pregenerator.resume();
|
inst.pregenerator.resume();
|
||||||
} else {
|
} else {
|
||||||
instance.pregenerator.pause();
|
inst.pregenerator.pause();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPaused() {
|
public static boolean isPaused() {
|
||||||
if (instance == null) {
|
PregeneratorJob inst = instance.get();
|
||||||
|
if (inst == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance.paused();
|
return inst.paused();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Color parseColor(String c) {
|
private static Color parseColor(String c) {
|
||||||
@@ -162,7 +173,7 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void drawRegion(int x, int z, Color color) {
|
public void drawRegion(int x, int z, Color color) {
|
||||||
J.a(() -> PregenTask.iterateRegion(x, z, (xx, zz) -> {
|
J.a(() -> task.iterateChunks(x, z, (xx, zz) -> {
|
||||||
draw(xx, zz, color);
|
draw(xx, zz, color);
|
||||||
J.sleep(3);
|
J.sleep(3);
|
||||||
}));
|
}));
|
||||||
@@ -182,7 +193,7 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
J.a(() -> {
|
J.a(() -> {
|
||||||
pregenerator.close();
|
pregenerator.close();
|
||||||
close();
|
close();
|
||||||
instance = null;
|
instance.compareAndSet(this, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,10 +233,10 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
|
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
|
||||||
info = new String[]{
|
info = new String[]{
|
||||||
(paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)",
|
(paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)",
|
||||||
"Speed: " + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",
|
"Speed: " + (cached ? "Cached " : "") + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",
|
||||||
Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)",
|
Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)",
|
||||||
"Generation Method: " + method,
|
"Generation Method: " + method,
|
||||||
"Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s",
|
"Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s",
|
||||||
@@ -243,13 +254,16 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChunkGenerated(int x, int z) {
|
public void onChunkGenerated(int x, int z, boolean cached) {
|
||||||
if (engine != null) {
|
if (renderer == null || frame == null || !frame.isVisible()) return;
|
||||||
draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
|
service.submit(() -> {
|
||||||
return;
|
if (engine != null) {
|
||||||
}
|
draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
draw(x, z, COLOR_GENERATED);
|
draw(x, z, COLOR_GENERATED);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -307,8 +321,9 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onClose() {
|
public void onClose() {
|
||||||
close();
|
close();
|
||||||
instance = null;
|
instance.compareAndSet(this, null);
|
||||||
whenDone.forEach(Runnable::run);
|
whenDone.forEach(Runnable::run);
|
||||||
|
service.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -34,10 +34,11 @@ public class INMS {
|
|||||||
"1.21.1", "v1_21_R1",
|
"1.21.1", "v1_21_R1",
|
||||||
"1.21.2", "v1_21_R2",
|
"1.21.2", "v1_21_R2",
|
||||||
"1.21.3", "v1_21_R2",
|
"1.21.3", "v1_21_R2",
|
||||||
"1.21.4", "v1_21_R3"
|
"1.21.4", "v1_21_R3",
|
||||||
|
"1.21.5", "v1_21_R4"
|
||||||
);
|
);
|
||||||
private static final List<Version> PACKS = List.of(
|
private static final List<Version> PACKS = List.of(
|
||||||
new Version(21, 4, "31010"),
|
new Version(21, 4, "31020"),
|
||||||
new Version(21, 2, "31000"),
|
new Version(21, 2, "31000"),
|
||||||
new Version(20, 1, "3910")
|
new Version(20, 1, "3910")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package com.volmit.iris.core.nms;
|
|||||||
|
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.container.Pair;
|
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -32,15 +31,12 @@ import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess;
|
|||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
import org.bukkit.generator.structure.Structure;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
|
||||||
public interface INMSBinding {
|
public interface INMSBinding {
|
||||||
@@ -93,7 +89,11 @@ public interface INMSBinding {
|
|||||||
MCABiomeContainer newBiomeContainer(int min, int max);
|
MCABiomeContainer newBiomeContainer(int min, int max);
|
||||||
|
|
||||||
default World createWorld(WorldCreator c) {
|
default World createWorld(WorldCreator c) {
|
||||||
|
if (missingDimensionTypes(true, true, true))
|
||||||
|
throw new IllegalStateException("Missing dimenstion types to create world");
|
||||||
|
|
||||||
try (var ignored = injectLevelStems()) {
|
try (var ignored = injectLevelStems()) {
|
||||||
|
ignored.storeContext();
|
||||||
return c.createWorld();
|
return c.createWorld();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,11 +130,13 @@ public interface INMSBinding {
|
|||||||
|
|
||||||
KList<String> getStructureKeys();
|
KList<String> getStructureKeys();
|
||||||
|
|
||||||
default AutoClosing injectLevelStems() {
|
AutoClosing injectLevelStems();
|
||||||
return new AutoClosing(() -> {});
|
|
||||||
|
default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
default Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
|
||||||
return new Pair<>(0, injectLevelStems());
|
|
||||||
}
|
void removeCustomDimensions(World world);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.volmit.iris.core.nms.container;
|
package com.volmit.iris.core.nms.container;
|
||||||
|
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.function.NastyRunnable;
|
import com.volmit.iris.util.function.NastyRunnable;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
@@ -7,6 +8,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class AutoClosing implements AutoCloseable {
|
public class AutoClosing implements AutoCloseable {
|
||||||
|
private static final KMap<Thread, AutoClosing> CONTEXTS = new KMap<>();
|
||||||
private final AtomicBoolean closed = new AtomicBoolean();
|
private final AtomicBoolean closed = new AtomicBoolean();
|
||||||
private final NastyRunnable action;
|
private final NastyRunnable action;
|
||||||
|
|
||||||
@@ -14,9 +16,24 @@ public class AutoClosing implements AutoCloseable {
|
|||||||
public void close() {
|
public void close() {
|
||||||
if (closed.getAndSet(true)) return;
|
if (closed.getAndSet(true)) return;
|
||||||
try {
|
try {
|
||||||
|
removeContext();
|
||||||
action.run();
|
action.run();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void storeContext() {
|
||||||
|
CONTEXTS.put(Thread.currentThread(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeContext() {
|
||||||
|
CONTEXTS.values().removeIf(c -> c == this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void closeContext() {
|
||||||
|
AutoClosing closing = CONTEXTS.remove(Thread.currentThread());
|
||||||
|
if (closing == null) return;
|
||||||
|
closing.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ package com.volmit.iris.core.nms.v1X;
|
|||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
@@ -118,6 +120,26 @@ public class NMSBinding1X implements INMSBinding {
|
|||||||
return new KList<>(list);
|
return new KList<>(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return new AutoClosing(() -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
return injectLevelStems();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
public CompoundTag serializeEntity(Entity location) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
public class ChunkUpdater {
|
public class ChunkUpdater {
|
||||||
|
private static final String REGION_PATH = "region" + File.separator + "r.";
|
||||||
private final AtomicBoolean paused = new AtomicBoolean();
|
private final AtomicBoolean paused = new AtomicBoolean();
|
||||||
private final AtomicBoolean cancelled = new AtomicBoolean();
|
private final AtomicBoolean cancelled = new AtomicBoolean();
|
||||||
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
|
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
|
||||||
@@ -162,11 +163,14 @@ public class ChunkUpdater {
|
|||||||
J.sleep(50);
|
J.sleep(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rX < dimensions.min.getX() || rX > dimensions.max.getX() || rZ < dimensions.min.getZ() || rZ > dimensions.max.getZ()) {
|
if (rX < dimensions.min.getX() ||
|
||||||
return;
|
rX > dimensions.max.getX() ||
|
||||||
}
|
rZ < dimensions.min.getZ() ||
|
||||||
|
rZ > dimensions.max.getZ() ||
|
||||||
|
!new File(world.getWorldFolder(), REGION_PATH + rX + "." + rZ + ".mca").exists()
|
||||||
|
) return;
|
||||||
|
|
||||||
PregenTask.iterateRegion(rX, rZ, (x, z) -> {
|
task.iterateChunks(rX, rZ, (x, z) -> {
|
||||||
while (paused.get() && !cancelled.get()) {
|
while (paused.get() && !cancelled.get()) {
|
||||||
J.sleep(50);
|
J.sleep(50);
|
||||||
}
|
}
|
||||||
@@ -348,8 +352,8 @@ public class ChunkUpdater {
|
|||||||
int width = maxZ - minZ + 1;
|
int width = maxZ - minZ + 1;
|
||||||
|
|
||||||
return new Dimensions(new Position2(minX, minZ), new Position2(maxX, maxZ), height * width, PregenTask.builder()
|
return new Dimensions(new Position2(minX, minZ), new Position2(maxX, maxZ), height * width, PregenTask.builder()
|
||||||
.width((int) Math.ceil(width / 2d))
|
.radiusZ((int) Math.ceil(width / 2d * 512))
|
||||||
.height((int) Math.ceil(height / 2d))
|
.radiusX((int) Math.ceil(height / 2d * 512))
|
||||||
.center(new Position2(oX, oZ))
|
.center(new Position2(oX, oZ))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
package com.volmit.iris.core.pregenerator;
|
package com.volmit.iris.core.pregenerator;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.pack.IrisPack;
|
|
||||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
@@ -32,28 +31,33 @@ import com.volmit.iris.util.math.RollingSequence;
|
|||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import com.volmit.iris.util.scheduling.Looper;
|
import com.volmit.iris.util.scheduling.Looper;
|
||||||
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
|
||||||
public class IrisPregenerator {
|
public class IrisPregenerator {
|
||||||
|
private static final double INVALID = 9223372036854775807d;
|
||||||
private final PregenTask task;
|
private final PregenTask task;
|
||||||
private final PregeneratorMethod generator;
|
private final PregeneratorMethod generator;
|
||||||
private final PregenListener listener;
|
private final PregenListener listener;
|
||||||
private final Looper ticker;
|
private final Looper ticker;
|
||||||
private final AtomicBoolean paused;
|
private final AtomicBoolean paused;
|
||||||
private final AtomicBoolean shutdown;
|
private final AtomicBoolean shutdown;
|
||||||
|
private final RollingSequence cachedPerSecond;
|
||||||
private final RollingSequence chunksPerSecond;
|
private final RollingSequence chunksPerSecond;
|
||||||
private final RollingSequence chunksPerMinute;
|
private final RollingSequence chunksPerMinute;
|
||||||
private final RollingSequence regionsPerMinute;
|
private final RollingSequence regionsPerMinute;
|
||||||
private final KList<Integer> chunksPerSecondHistory;
|
private final KList<Integer> chunksPerSecondHistory;
|
||||||
private static AtomicInteger generated;
|
private final AtomicLong generated;
|
||||||
private final AtomicInteger generatedLast;
|
private final AtomicLong generatedLast;
|
||||||
private final AtomicInteger generatedLastMinute;
|
private final AtomicLong generatedLastMinute;
|
||||||
private static AtomicInteger totalChunks;
|
private final AtomicLong cached;
|
||||||
|
private final AtomicLong cachedLast;
|
||||||
|
private final AtomicLong cachedLastMinute;
|
||||||
|
private final AtomicLong totalChunks;
|
||||||
private final AtomicLong startTime;
|
private final AtomicLong startTime;
|
||||||
private final ChronoLatch minuteLatch;
|
private final ChronoLatch minuteLatch;
|
||||||
private final AtomicReference<String> currentGeneratorMethod;
|
private final AtomicReference<String> currentGeneratorMethod;
|
||||||
@@ -75,46 +79,71 @@ public class IrisPregenerator {
|
|||||||
net = new KSet<>();
|
net = new KSet<>();
|
||||||
currentGeneratorMethod = new AtomicReference<>("Void");
|
currentGeneratorMethod = new AtomicReference<>("Void");
|
||||||
minuteLatch = new ChronoLatch(60000, false);
|
minuteLatch = new ChronoLatch(60000, false);
|
||||||
|
cachedPerSecond = new RollingSequence(5);
|
||||||
chunksPerSecond = new RollingSequence(10);
|
chunksPerSecond = new RollingSequence(10);
|
||||||
chunksPerMinute = new RollingSequence(10);
|
chunksPerMinute = new RollingSequence(10);
|
||||||
regionsPerMinute = new RollingSequence(10);
|
regionsPerMinute = new RollingSequence(10);
|
||||||
chunksPerSecondHistory = new KList<>();
|
chunksPerSecondHistory = new KList<>();
|
||||||
generated = new AtomicInteger(0);
|
generated = new AtomicLong(0);
|
||||||
generatedLast = new AtomicInteger(0);
|
generatedLast = new AtomicLong(0);
|
||||||
generatedLastMinute = new AtomicInteger(0);
|
generatedLastMinute = new AtomicLong(0);
|
||||||
totalChunks = new AtomicInteger(0);
|
cached = new AtomicLong();
|
||||||
task.iterateRegions((_a, _b) -> totalChunks.addAndGet(1024));
|
cachedLast = new AtomicLong(0);
|
||||||
|
cachedLastMinute = new AtomicLong(0);
|
||||||
|
totalChunks = new AtomicLong(0);
|
||||||
|
task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet());
|
||||||
startTime = new AtomicLong(M.ms());
|
startTime = new AtomicLong(M.ms());
|
||||||
ticker = new Looper() {
|
ticker = new Looper() {
|
||||||
@Override
|
@Override
|
||||||
protected long loop() {
|
protected long loop() {
|
||||||
long eta = computeETA();
|
long eta = computeETA();
|
||||||
int secondGenerated = generated.get() - generatedLast.get();
|
|
||||||
generatedLast.set(generated.get());
|
|
||||||
chunksPerSecond.put(secondGenerated);
|
|
||||||
chunksPerSecondHistory.add(secondGenerated);
|
|
||||||
|
|
||||||
if (minuteLatch.flip()) {
|
long secondCached = cached.get() - cachedLast.get();
|
||||||
int minuteGenerated = generated.get() - generatedLastMinute.get();
|
cachedLast.set(cached.get());
|
||||||
generatedLastMinute.set(generated.get());
|
cachedPerSecond.put(secondCached);
|
||||||
chunksPerMinute.put(minuteGenerated);
|
|
||||||
regionsPerMinute.put((double) minuteGenerated / 1024D);
|
long secondGenerated = generated.get() - generatedLast.get() - secondCached;
|
||||||
|
generatedLast.set(generated.get());
|
||||||
|
if (secondCached == 0 || secondGenerated != 0) {
|
||||||
|
chunksPerSecond.put(secondGenerated);
|
||||||
|
chunksPerSecondHistory.add((int) secondGenerated);
|
||||||
}
|
}
|
||||||
|
|
||||||
listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(),
|
if (minuteLatch.flip()) {
|
||||||
|
long minuteCached = cached.get() - cachedLastMinute.get();
|
||||||
|
cachedLastMinute.set(cached.get());
|
||||||
|
|
||||||
|
long minuteGenerated = generated.get() - generatedLastMinute.get() - minuteCached;
|
||||||
|
generatedLastMinute.set(generated.get());
|
||||||
|
if (minuteCached == 0 || minuteGenerated != 0) {
|
||||||
|
chunksPerMinute.put(minuteGenerated);
|
||||||
|
regionsPerMinute.put((double) minuteGenerated / 1024D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean cached = cachedPerSecond.getAverage() != 0;
|
||||||
|
|
||||||
|
listener.onTick(
|
||||||
|
cached ? cachedPerSecond.getAverage() : chunksPerSecond.getAverage(),
|
||||||
|
chunksPerMinute.getAverage(),
|
||||||
regionsPerMinute.getAverage(),
|
regionsPerMinute.getAverage(),
|
||||||
(double) generated.get() / (double) totalChunks.get(),
|
(double) generated.get() / (double) totalChunks.get(), generated.get(),
|
||||||
generated.get(), totalChunks.get(),
|
totalChunks.get(),
|
||||||
totalChunks.get() - generated.get(),
|
totalChunks.get() - generated.get(), eta, M.ms() - startTime.get(), currentGeneratorMethod.get(),
|
||||||
eta, M.ms() - startTime.get(), currentGeneratorMethod.get());
|
cached);
|
||||||
|
|
||||||
if (cl.flip()) {
|
if (cl.flip()) {
|
||||||
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
|
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
|
||||||
if (!IrisPackBenchmarking.benchmarkInProgress) {
|
|
||||||
Iris.info("Pregen: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage);
|
Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s",
|
||||||
} else {
|
IrisPackBenchmarking.benchmarkInProgress ? "Benchmarking" : "Pregen",
|
||||||
Iris.info("Benchmarking: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage);
|
Form.f(generated.get()),
|
||||||
}
|
Form.f(totalChunks.get()),
|
||||||
|
percentage,
|
||||||
|
cached ?
|
||||||
|
"Cached " + Form.f((int) cachedPerSecond.getAverage()) :
|
||||||
|
Form.f((int) chunksPerSecond.getAverage()),
|
||||||
|
Form.duration(eta, 2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
@@ -122,12 +151,13 @@ public class IrisPregenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long computeETA() {
|
private long computeETA() {
|
||||||
return (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
|
double d = (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
|
||||||
// If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers)
|
// If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers)
|
||||||
((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) :
|
((totalChunks.get() - generated.get() - cached.get()) * ((double) (M.ms() - startTime.get()) / ((double) generated.get() - cached.get()))) :
|
||||||
// If no, use quick function (which is less accurate over time but responds better to the initial delay)
|
// If no, use quick function (which is less accurate over time but responds better to the initial delay)
|
||||||
((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000
|
((totalChunks.get() - generated.get() - cached.get()) / chunksPerSecond.getAverage()) * 1000
|
||||||
);
|
);
|
||||||
|
return Double.isFinite(d) && d != INVALID ? (long) d : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,8 +169,10 @@ public class IrisPregenerator {
|
|||||||
init();
|
init();
|
||||||
ticker.start();
|
ticker.start();
|
||||||
checkRegions();
|
checkRegions();
|
||||||
|
var p = PrecisionStopwatch.start();
|
||||||
task.iterateRegions((x, z) -> visitRegion(x, z, true));
|
task.iterateRegions((x, z) -> visitRegion(x, z, true));
|
||||||
task.iterateRegions((x, z) -> visitRegion(x, z, false));
|
task.iterateRegions((x, z) -> visitRegion(x, z, false));
|
||||||
|
Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
|
||||||
shutdown();
|
shutdown();
|
||||||
if (!IrisPackBenchmarking.benchmarkInProgress) {
|
if (!IrisPackBenchmarking.benchmarkInProgress) {
|
||||||
Iris.info(C.IRIS + "Pregen stopped.");
|
Iris.info(C.IRIS + "Pregen stopped.");
|
||||||
@@ -194,7 +226,7 @@ public class IrisPregenerator {
|
|||||||
} else if (!regions) {
|
} else if (!regions) {
|
||||||
hit = true;
|
hit = true;
|
||||||
listener.onRegionGenerating(x, z);
|
listener.onRegionGenerating(x, z);
|
||||||
PregenTask.iterateRegion(x, z, (xx, zz) -> {
|
task.iterateChunks(x, z, (xx, zz) -> {
|
||||||
while (paused.get() && !shutdown.get()) {
|
while (paused.get() && !shutdown.get()) {
|
||||||
J.sleep(50);
|
J.sleep(50);
|
||||||
}
|
}
|
||||||
@@ -235,8 +267,8 @@ public class IrisPregenerator {
|
|||||||
private PregenListener listenify(PregenListener listener) {
|
private PregenListener listenify(PregenListener listener) {
|
||||||
return new PregenListener() {
|
return new PregenListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
|
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
|
||||||
listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method);
|
listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method, cached);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -245,9 +277,10 @@ public class IrisPregenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChunkGenerated(int x, int z) {
|
public void onChunkGenerated(int x, int z, boolean c) {
|
||||||
listener.onChunkGenerated(x, z);
|
listener.onChunkGenerated(x, z, c);
|
||||||
generated.addAndGet(1);
|
generated.addAndGet(1);
|
||||||
|
if (c) cached.addAndGet(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,11 +19,15 @@
|
|||||||
package com.volmit.iris.core.pregenerator;
|
package com.volmit.iris.core.pregenerator;
|
||||||
|
|
||||||
public interface PregenListener {
|
public interface PregenListener {
|
||||||
void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method);
|
void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached);
|
||||||
|
|
||||||
void onChunkGenerating(int x, int z);
|
void onChunkGenerating(int x, int z);
|
||||||
|
|
||||||
void onChunkGenerated(int x, int z);
|
default void onChunkGenerated(int x, int z) {
|
||||||
|
onChunkGenerated(x, z, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChunkGenerated(int x, int z, boolean cached);
|
||||||
|
|
||||||
void onRegionGenerated(int x, int z);
|
void onRegionGenerated(int x, int z);
|
||||||
|
|
||||||
|
|||||||
@@ -32,17 +32,26 @@ import java.util.Comparator;
|
|||||||
@Data
|
@Data
|
||||||
public class PregenTask {
|
public class PregenTask {
|
||||||
private static final Position2 ZERO = new Position2(0, 0);
|
private static final Position2 ZERO = new Position2(0, 0);
|
||||||
private static final KList<Position2> ORDER_CENTER = computeChunkOrder();
|
|
||||||
private static final KMap<Position2, KList<Position2>> ORDERS = new KMap<>();
|
private static final KMap<Position2, KList<Position2>> ORDERS = new KMap<>();
|
||||||
|
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean gui = false;
|
private final boolean gui = false;
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Position2 center = new Position2(0, 0);
|
private final Position2 center = new Position2(0, 0);
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private int width = 1;
|
private final int radiusX = 1;
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private int height = 1;
|
private final int radiusZ = 1;
|
||||||
|
|
||||||
|
private final Bounds bounds = new Bounds();
|
||||||
|
|
||||||
|
protected PregenTask(boolean gui, Position2 center, int radiusX, int radiusZ) {
|
||||||
|
this.gui = gui;
|
||||||
|
this.center = new ProxiedPos(center);
|
||||||
|
this.radiusX = radiusX;
|
||||||
|
this.radiusZ = radiusZ;
|
||||||
|
bounds.update();
|
||||||
|
}
|
||||||
|
|
||||||
public static void iterateRegion(int xr, int zr, Spiraled s, Position2 pull) {
|
public static void iterateRegion(int xr, int zr, Spiraled s, Position2 pull) {
|
||||||
for (Position2 i : ORDERS.computeIfAbsent(pull, PregenTask::computeOrder)) {
|
for (Position2 i : ORDERS.computeIfAbsent(pull, PregenTask::computeOrder)) {
|
||||||
@@ -70,29 +79,72 @@ public class PregenTask {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static KList<Position2> computeChunkOrder() {
|
public void iterateRegions(Spiraled s) {
|
||||||
Position2 center = new Position2(15, 15);
|
var bound = bounds.region();
|
||||||
KList<Position2> p = new KList<>();
|
new Spiraler(bound.sizeX, bound.sizeZ, ((x, z) -> {
|
||||||
new Spiraler(33, 33, (x, z) -> {
|
if (bound.check(x, z)) s.on(x, z);
|
||||||
int xx = x + 15;
|
})).setOffset(center.getX() >> 9, center.getZ() >> 9).drain();
|
||||||
int zz = z + 15;
|
|
||||||
if (xx < 0 || xx > 31 || zz < 0 || zz > 31) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.add(new Position2(xx, zz));
|
|
||||||
}).drain();
|
|
||||||
p.sort(Comparator.comparing((i) -> i.distance(center)));
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void iterateRegions(Spiraled s) {
|
public void iterateChunks(int rX, int rZ, Spiraled s) {
|
||||||
new Spiraler(getWidth() * 2, getHeight() * 2, s)
|
var bound = bounds.chunk();
|
||||||
.setOffset(center.getX(), center.getZ()).drain();
|
iterateRegion(rX, rZ, ((x, z) -> {
|
||||||
|
if (bound.check(x, z)) s.on(x, z);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void iterateAllChunks(Spiraled s) {
|
public void iterateAllChunks(Spiraled s) {
|
||||||
new Spiraler(getWidth() * 2, getHeight() * 2, (x, z) -> iterateRegion(x, z, s))
|
iterateRegions(((rX, rZ) -> iterateChunks(rX, rZ, s)));
|
||||||
.setOffset(center.getX(), center.getZ()).drain();
|
}
|
||||||
|
|
||||||
|
private class Bounds {
|
||||||
|
private Bound chunk = null;
|
||||||
|
private Bound region = null;
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
int maxX = center.getX() + radiusX;
|
||||||
|
int maxZ = center.getZ() + radiusZ;
|
||||||
|
int minX = center.getX() - radiusX;
|
||||||
|
int minZ = center.getZ() - radiusZ;
|
||||||
|
|
||||||
|
chunk = new Bound(minX >> 4, minZ >> 4, Math.ceilDiv(maxX, 16), Math.ceilDiv(maxZ, 16));
|
||||||
|
region = new Bound(minX >> 9, minZ >> 9, Math.ceilDiv(maxX, 512), Math.ceilDiv(maxZ, 512));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bound chunk() {
|
||||||
|
if (chunk == null) update();
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bound region() {
|
||||||
|
if (region == null) update();
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Bound(int minX, int maxX, int minZ, int maxZ, int sizeX, int sizeZ) {
|
||||||
|
private Bound(int minX, int minZ, int maxX, int maxZ) {
|
||||||
|
this(minX, maxX, minZ, maxZ, maxZ - minZ + 1, maxZ - minZ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean check(int x, int z) {
|
||||||
|
return x >= minX && x <= maxX && z >= minZ && z <= maxZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ProxiedPos extends Position2 {
|
||||||
|
public ProxiedPos(Position2 p) {
|
||||||
|
super(p.getX(), p.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setX(int x) {
|
||||||
|
throw new IllegalStateException("This Position2 may not be modified");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setZ(int z) {
|
||||||
|
throw new IllegalStateException("This Position2 may not be modified");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.volmit.iris.core.pregenerator.cache;
|
||||||
|
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public interface PregenCache {
|
||||||
|
default boolean isThreadSafe() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
boolean isChunkCached(int x, int z);
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
boolean isRegionCached(int x, int z);
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
void cacheChunk(int x, int z);
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
void cacheRegion(int x, int z);
|
||||||
|
|
||||||
|
void write();
|
||||||
|
|
||||||
|
static PregenCache create(File directory) {
|
||||||
|
if (directory == null) return EMPTY;
|
||||||
|
return new PregenCacheImpl(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
default PregenCache sync() {
|
||||||
|
if (isThreadSafe()) return this;
|
||||||
|
return new SynchronizedCache(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PregenCache EMPTY = new PregenCache() {
|
||||||
|
@Override
|
||||||
|
public boolean isThreadSafe() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChunkCached(int x, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegionCached(int x, int z) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cacheChunk(int x, int z) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cacheRegion(int x, int z) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+215
@@ -0,0 +1,215 @@
|
|||||||
|
package com.volmit.iris.core.pregenerator.cache;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
|
import com.github.benmanes.caffeine.cache.RemovalCause;
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.util.data.Varint;
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
|
import com.volmit.iris.util.parallel.HyperLock;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||||
|
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
class PregenCacheImpl implements PregenCache {
|
||||||
|
private static final int SIZE = 32;
|
||||||
|
private final File directory;
|
||||||
|
private final HyperLock hyperLock = new HyperLock(SIZE * 2, true);
|
||||||
|
private final LoadingCache<Pos, Plate> cache = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(10, TimeUnit.SECONDS)
|
||||||
|
.maximumSize(SIZE)
|
||||||
|
.removalListener(this::onRemoval)
|
||||||
|
.evictionListener(this::onRemoval)
|
||||||
|
.build(this::load);
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public boolean isChunkCached(int x, int z) {
|
||||||
|
var plate = cache.get(new Pos(x >> 10, z >> 10));
|
||||||
|
if (plate == null) return false;
|
||||||
|
return plate.isCached((x >> 5) & 31, (z >> 5) & 31, r -> r.isCached(x & 31, z & 31));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
public boolean isRegionCached(int x, int z) {
|
||||||
|
var plate = cache.get(new Pos(x >> 5, z >> 5));
|
||||||
|
if (plate == null) return false;
|
||||||
|
return plate.isCached(x & 31, z & 31, Region::isCached);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public void cacheChunk(int x, int z) {
|
||||||
|
var plate = cache.get(new Pos(x >> 10, z >> 10));
|
||||||
|
plate.cache((x >> 5) & 31, (z >> 5) & 31, r -> r.cache(x & 31, z & 31));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegionCoordinates
|
||||||
|
public void cacheRegion(int x, int z) {
|
||||||
|
var plate = cache.get(new Pos(x >> 5, z >> 5));
|
||||||
|
plate.cache(x & 31, z & 31, Region::cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write() {
|
||||||
|
cache.asMap().values().forEach(this::write);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Plate load(Pos key) {
|
||||||
|
hyperLock.lock(key.x, key.z);
|
||||||
|
try {
|
||||||
|
File file = fileForPlate(key);
|
||||||
|
if (!file.exists()) return new Plate(key);
|
||||||
|
try (var in = new DataInputStream(new LZ4BlockInputStream(new FileInputStream(file)))) {
|
||||||
|
return new Plate(key, in);
|
||||||
|
} catch (IOException e){
|
||||||
|
Iris.error("Failed to read pregen cache " + file);
|
||||||
|
e.printStackTrace();
|
||||||
|
return new Plate(key);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
hyperLock.unlock(key.x, key.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(Plate plate) {
|
||||||
|
hyperLock.lock(plate.pos.x, plate.pos.z);
|
||||||
|
try {
|
||||||
|
File file = fileForPlate(plate.pos);
|
||||||
|
try (var out = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) {
|
||||||
|
plate.write(out);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Iris.error("Failed to write pregen cache " + file);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
hyperLock.unlock(plate.pos.x, plate.pos.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRemoval(@Nullable Pos key, @Nullable Plate plate, RemovalCause cause) {
|
||||||
|
if (plate == null) return;
|
||||||
|
write(plate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File fileForPlate(Pos pos) {
|
||||||
|
if (!directory.exists() && !directory.mkdirs())
|
||||||
|
throw new IllegalStateException("Cannot create directory: " + directory.getAbsolutePath());
|
||||||
|
return new File(directory, "c." + pos.x + "." + pos.z + ".lz4b");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Plate {
|
||||||
|
private final Pos pos;
|
||||||
|
private short count;
|
||||||
|
private Region[] regions;
|
||||||
|
|
||||||
|
public Plate(Pos pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
count = 0;
|
||||||
|
regions = new Region[1024];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plate(Pos pos, DataInput in) throws IOException {
|
||||||
|
this.pos = pos;
|
||||||
|
count = (short) Varint.readSignedVarInt(in);
|
||||||
|
if (count == 1024) return;
|
||||||
|
regions = new Region[1024];
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
if (in.readBoolean()) continue;
|
||||||
|
regions[i] = new Region(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCached(int x, int z, Predicate<Region> predicate) {
|
||||||
|
if (count == 1024) return true;
|
||||||
|
Region region = regions[x * 32 + z];
|
||||||
|
if (region == null) return false;
|
||||||
|
return predicate.test(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cache(int x, int z, Predicate<Region> predicate) {
|
||||||
|
if (count == 1024) return;
|
||||||
|
Region region = regions[x * 32 + z];
|
||||||
|
if (region == null) regions[x * 32 + z] = region = new Region();
|
||||||
|
if (predicate.test(region)) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(DataOutput out) throws IOException {
|
||||||
|
Varint.writeSignedVarInt(count, out);
|
||||||
|
if (count == 1024) return;
|
||||||
|
for (Region region : regions) {
|
||||||
|
out.writeBoolean(region == null);
|
||||||
|
if (region == null) continue;
|
||||||
|
region.write(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Region {
|
||||||
|
private short count;
|
||||||
|
private long[] words;
|
||||||
|
|
||||||
|
public Region() {
|
||||||
|
count = 0;
|
||||||
|
words = new long[64];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Region(DataInput in) throws IOException {
|
||||||
|
count = (short) Varint.readSignedVarInt(in);
|
||||||
|
if (count == 1024) return;
|
||||||
|
words = new long[64];
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
words[i] = Varint.readUnsignedVarLong(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cache() {
|
||||||
|
if (count == 1024) return false;
|
||||||
|
count = 1024;
|
||||||
|
words = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cache(int x, int z) {
|
||||||
|
if (count == 1024) return false;
|
||||||
|
|
||||||
|
int i = x * 32 + z;
|
||||||
|
int w = i >> 6;
|
||||||
|
long b = 1L << (i & 63);
|
||||||
|
|
||||||
|
var cur = (words[w] & b) != 0;
|
||||||
|
if (cur) return false;
|
||||||
|
|
||||||
|
if (++count == 1024) {
|
||||||
|
words = null;
|
||||||
|
return true;
|
||||||
|
} else words[w] |= b;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCached() {
|
||||||
|
return count == 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCached(int x, int z) {
|
||||||
|
int i = x * 32 + z;
|
||||||
|
return count == 1024 || (words[i >> 6] & 1L << (i & 63)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(DataOutput out) throws IOException {
|
||||||
|
Varint.writeSignedVarInt(count, out);
|
||||||
|
if (isCached()) return;
|
||||||
|
for (long word : words) {
|
||||||
|
Varint.writeUnsignedVarLong(word, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Pos(int x, int z) {}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package com.volmit.iris.core.pregenerator.cache;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
class SynchronizedCache implements PregenCache {
|
||||||
|
private final PregenCache cache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isThreadSafe() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChunkCached(int x, int z) {
|
||||||
|
synchronized (cache) {
|
||||||
|
return cache.isChunkCached(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegionCached(int x, int z) {
|
||||||
|
synchronized (cache) {
|
||||||
|
return cache.isRegionCached(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cacheChunk(int x, int z) {
|
||||||
|
synchronized (cache) {
|
||||||
|
cache.cacheChunk(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cacheRegion(int x, int z) {
|
||||||
|
synchronized (cache) {
|
||||||
|
cache.cacheRegion(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write() {
|
||||||
|
synchronized (cache) {
|
||||||
|
cache.write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+120
-29
@@ -19,6 +19,7 @@
|
|||||||
package com.volmit.iris.core.pregenerator.methods;
|
package com.volmit.iris.core.pregenerator.methods;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
@@ -31,24 +32,32 @@ import io.papermc.lib.PaperLib;
|
|||||||
import org.bukkit.Chunk;
|
import org.bukkit.Chunk;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class AsyncPregenMethod implements PregeneratorMethod {
|
public class AsyncPregenMethod implements PregeneratorMethod {
|
||||||
|
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
|
||||||
private final World world;
|
private final World world;
|
||||||
private final MultiBurst burst;
|
private final Executor executor;
|
||||||
private final Semaphore semaphore;
|
private final Semaphore semaphore;
|
||||||
|
private final int threads;
|
||||||
|
private final boolean urgent;
|
||||||
private final Map<Chunk, Long> lastUse;
|
private final Map<Chunk, Long> lastUse;
|
||||||
|
|
||||||
public AsyncPregenMethod(World world, int threads) {
|
public AsyncPregenMethod(World world, int unusedThreads) {
|
||||||
if (!PaperLib.isPaper()) {
|
if (!PaperLib.isPaper()) {
|
||||||
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.world = world;
|
this.world = world;
|
||||||
burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor();
|
||||||
semaphore = new Semaphore(256);
|
this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
|
||||||
|
this.semaphore = new Semaphore(this.threads, true);
|
||||||
|
this.urgent = IrisSettings.get().getPregen().useHighPriority;
|
||||||
this.lastUse = new KMap<>();
|
this.lastUse = new KMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,13 +69,18 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Chunk i : new ArrayList<>(lastUse.keySet())) {
|
long minTime = M.ms() - 10_000;
|
||||||
Long lastUseTime = lastUse.get(i);
|
lastUse.entrySet().removeIf(i -> {
|
||||||
if (!i.isLoaded() || (lastUseTime != null && M.ms() - lastUseTime >= 10000)) {
|
final Chunk chunk = i.getKey();
|
||||||
i.unload();
|
final Long lastUseTime = i.getValue();
|
||||||
lastUse.remove(i);
|
if (!chunk.isLoaded() || lastUseTime == null)
|
||||||
|
return true;
|
||||||
|
if (lastUseTime < minTime) {
|
||||||
|
chunk.unload();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
world.save();
|
world.save();
|
||||||
}).get();
|
}).get();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@@ -74,24 +88,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void completeChunk(int x, int z, PregenListener listener) {
|
|
||||||
try {
|
|
||||||
PaperLib.getChunkAtAsync(world, x, z, true).thenAccept((i) -> {
|
|
||||||
lastUse.put(i, M.ms());
|
|
||||||
listener.onChunkGenerated(x, z);
|
|
||||||
listener.onChunkCleaned(x, z);
|
|
||||||
}).get();
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
semaphore.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
unloadAndSaveAllChunks();
|
unloadAndSaveAllChunks();
|
||||||
|
increaseWorkerThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,9 +101,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
semaphore.acquireUninterruptibly(256);
|
semaphore.acquireUninterruptibly(threads);
|
||||||
unloadAndSaveAllChunks();
|
unloadAndSaveAllChunks();
|
||||||
burst.close();
|
executor.shutdown();
|
||||||
|
resetWorkerThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -129,7 +130,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
burst.complete(() -> completeChunk(x, z, listener));
|
executor.generate(x, z, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -140,4 +141,94 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void increaseWorkerThreads() {
|
||||||
|
THREAD_COUNT.updateAndGet(i -> {
|
||||||
|
if (i > 0) return 1;
|
||||||
|
var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
|
||||||
|
try {
|
||||||
|
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
|
||||||
|
var pool = field.get(null);
|
||||||
|
var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length;
|
||||||
|
if (threads >= adjusted) return 0;
|
||||||
|
|
||||||
|
pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted);
|
||||||
|
return threads;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted);
|
||||||
|
Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads");
|
||||||
|
if (e instanceof InvocationTargetException) e.printStackTrace();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetWorkerThreads() {
|
||||||
|
THREAD_COUNT.updateAndGet(i -> {
|
||||||
|
if (i == 0) return 0;
|
||||||
|
try {
|
||||||
|
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
|
||||||
|
var pool = field.get(null);
|
||||||
|
var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class);
|
||||||
|
method.invoke(pool, i);
|
||||||
|
return 0;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Failed to reset worker threads");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Executor {
|
||||||
|
void generate(int x, int z, PregenListener listener);
|
||||||
|
default void shutdown() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ServiceExecutor implements Executor {
|
||||||
|
private final ExecutorService service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
|
||||||
|
Executors.newVirtualThreadPerTaskExecutor() :
|
||||||
|
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||||
|
|
||||||
|
public void generate(int x, int z, PregenListener listener) {
|
||||||
|
service.submit(() -> {
|
||||||
|
try {
|
||||||
|
PaperLib.getChunkAtAsync(world, x, z, true, urgent).thenAccept((i) -> {
|
||||||
|
listener.onChunkGenerated(x, z);
|
||||||
|
listener.onChunkCleaned(x, z);
|
||||||
|
if (i == null) return;
|
||||||
|
lastUse.put(i, M.ms());
|
||||||
|
}).get();
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
service.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TicketExecutor implements Executor {
|
||||||
|
@Override
|
||||||
|
public void generate(int x, int z, PregenListener listener) {
|
||||||
|
PaperLib.getChunkAtAsync(world, x, z, true, urgent)
|
||||||
|
.exceptionally(e -> {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.thenAccept(i -> {
|
||||||
|
semaphore.release();
|
||||||
|
listener.onChunkGenerated(x, z);
|
||||||
|
listener.onChunkCleaned(x, z);
|
||||||
|
if (i == null) return;
|
||||||
|
lastUse.put(i, M.ms());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.volmit.iris.core.pregenerator.methods;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregenListener;
|
||||||
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
|
import com.volmit.iris.core.pregenerator.cache.PregenCache;
|
||||||
|
import com.volmit.iris.core.service.GlobalCacheSVC;
|
||||||
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CachedPregenMethod implements PregeneratorMethod {
|
||||||
|
private final PregeneratorMethod method;
|
||||||
|
private final PregenCache cache;
|
||||||
|
|
||||||
|
public CachedPregenMethod(PregeneratorMethod method, String worldName) {
|
||||||
|
this.method = method;
|
||||||
|
var cache = Iris.service(GlobalCacheSVC.class).get(worldName);
|
||||||
|
if (cache == null) {
|
||||||
|
Iris.debug("Could not find existing cache for " + worldName + " creating fallback");
|
||||||
|
cache = GlobalCacheSVC.createDefault(worldName);
|
||||||
|
}
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
method.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
method.close();
|
||||||
|
cache.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
method.save();
|
||||||
|
cache.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRegions(int x, int z, PregenListener listener) {
|
||||||
|
return cache.isRegionCached(x, z) || method.supportsRegions(x, z, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethod(int x, int z) {
|
||||||
|
return method.getMethod(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateRegion(int x, int z, PregenListener listener) {
|
||||||
|
if (cache.isRegionCached(x, z)) {
|
||||||
|
listener.onRegionGenerated(x, z);
|
||||||
|
|
||||||
|
int rX = x << 5, rZ = z << 5;
|
||||||
|
for (int cX = 0; cX < 32; cX++) {
|
||||||
|
for (int cZ = 0; cZ < 32; cZ++) {
|
||||||
|
listener.onChunkGenerated(rX + cX, rZ + cZ, true);
|
||||||
|
listener.onChunkCleaned(rX + cX, rZ + cZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
method.generateRegion(x, z, listener);
|
||||||
|
cache.cacheRegion(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateChunk(int x, int z, PregenListener listener) {
|
||||||
|
if (cache.isChunkCached(x, z)) {
|
||||||
|
listener.onChunkGenerated(x, z, true);
|
||||||
|
listener.onChunkCleaned(x, z);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
method.generateChunk(x, z, listener);
|
||||||
|
cache.cacheChunk(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mantle getMantle() {
|
||||||
|
return method.getMantle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.volmit.iris.core.safeguard;
|
package com.volmit.iris.core.safeguard;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
|
||||||
public class IrisSafeguard {
|
public class IrisSafeguard {
|
||||||
public static boolean unstablemode = false;
|
public static boolean unstablemode = false;
|
||||||
@@ -11,5 +12,13 @@ public class IrisSafeguard {
|
|||||||
Iris.info("Enabled Iris SafeGuard");
|
Iris.info("Enabled Iris SafeGuard");
|
||||||
ServerBootSFG.BootCheck();
|
ServerBootSFG.BootCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void earlySplash() {
|
||||||
|
if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Iris.instance.splash();
|
||||||
|
UtilsSFG.splash();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.volmit.iris.core.safeguard;
|
|||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
|
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
|
||||||
|
import com.volmit.iris.engine.object.IrisContextInjector;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
@@ -29,6 +30,7 @@ public class ServerBootSFG {
|
|||||||
public static boolean isJRE = false;
|
public static boolean isJRE = false;
|
||||||
public static boolean hasPrivileges = true;
|
public static boolean hasPrivileges = true;
|
||||||
public static boolean unsuportedversion = false;
|
public static boolean unsuportedversion = false;
|
||||||
|
public static boolean missingDimensionTypes = false;
|
||||||
protected static boolean safeguardPassed;
|
protected static boolean safeguardPassed;
|
||||||
public static boolean passedserversoftware = true;
|
public static boolean passedserversoftware = true;
|
||||||
protected static int count;
|
protected static int count;
|
||||||
@@ -110,6 +112,12 @@ public class ServerBootSFG {
|
|||||||
severityMedium++;
|
severityMedium++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IrisContextInjector.isMissingDimensionTypes()) {
|
||||||
|
missingDimensionTypes = true;
|
||||||
|
joiner.add("Missing Dimension Types");
|
||||||
|
severityHigh++;
|
||||||
|
}
|
||||||
|
|
||||||
allIncompatibilities = joiner.toString();
|
allIncompatibilities = joiner.toString();
|
||||||
|
|
||||||
safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0);
|
safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0);
|
||||||
@@ -152,13 +160,9 @@ public class ServerBootSFG {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean enoughDiskSpace() {
|
public static boolean enoughDiskSpace() {
|
||||||
File freeSpace = new File(Bukkit.getWorldContainer() + ".");
|
File freeSpace = Bukkit.getWorldContainer();
|
||||||
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
|
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
|
||||||
if (gigabytes > 3){
|
return gigabytes > 3;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkJavac(String path) {
|
private static boolean checkJavac(String path) {
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ public class UtilsSFG {
|
|||||||
Iris.safeguard(C.RED + "Server Version");
|
Iris.safeguard(C.RED + "Server Version");
|
||||||
Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4");
|
Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4");
|
||||||
}
|
}
|
||||||
|
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.passedserversoftware) {
|
if (!ServerBootSFG.passedserversoftware) {
|
||||||
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
||||||
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");
|
Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead.");
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.volmit.iris.core.service;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.core.pregenerator.cache.PregenCache;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.plugin.IrisService;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.world.ChunkLoadEvent;
|
||||||
|
import org.bukkit.event.world.WorldInitEvent;
|
||||||
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class GlobalCacheSVC implements IrisService {
|
||||||
|
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build();
|
||||||
|
private final KMap<String, PregenCache> globalCache = new KMap<>();
|
||||||
|
private transient boolean lastState;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
lastState = !IrisSettings.get().getWorld().isGlobalPregenCache();
|
||||||
|
if (lastState) return;
|
||||||
|
Bukkit.getWorlds().forEach(this::createCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
globalCache.values().forEach(PregenCache::write);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public PregenCache get(@NonNull World world) {
|
||||||
|
return globalCache.get(world.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public PregenCache get(@NonNull String world) {
|
||||||
|
return globalCache.get(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void on(WorldInitEvent event) {
|
||||||
|
if (isDisabled()) return;
|
||||||
|
createCache(event.getWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void on(WorldUnloadEvent event) {
|
||||||
|
var cache = globalCache.remove(event.getWorld().getName());
|
||||||
|
if (cache == null) return;
|
||||||
|
cache.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void on(ChunkLoadEvent event) {
|
||||||
|
var cache = get(event.getWorld());
|
||||||
|
if (cache == null) return;
|
||||||
|
cache.cacheChunk(event.getChunk().getX(), event.getChunk().getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCache(World world) {
|
||||||
|
globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDisabled() {
|
||||||
|
boolean conf = IrisSettings.get().getWorld().isGlobalPregenCache();
|
||||||
|
if (lastState != conf)
|
||||||
|
return lastState;
|
||||||
|
|
||||||
|
if (conf) {
|
||||||
|
Bukkit.getWorlds().forEach(this::createCache);
|
||||||
|
} else {
|
||||||
|
globalCache.values().removeIf(cache -> {
|
||||||
|
cache.write();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastState = !conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static PregenCache createCache(@NonNull String worldName, @NonNull Function<String, PregenCache> provider) {
|
||||||
|
return REFERENCE_CACHE.get(worldName, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static PregenCache createDefault(@NonNull String worldName) {
|
||||||
|
return createCache(worldName, GlobalCacheSVC::createDefault0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PregenCache createDefault0(String worldName) {
|
||||||
|
return PregenCache.create(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pregen"))).sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,317 +1,242 @@
|
|||||||
package com.volmit.iris.core.service;
|
package com.volmit.iris.core.service;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.AtomicDouble;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.IrisSettings;
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
import com.volmit.iris.util.math.RNG;
|
||||||
import com.volmit.iris.util.misc.getHardware;
|
|
||||||
import com.volmit.iris.util.plugin.IrisService;
|
import com.volmit.iris.util.plugin.IrisService;
|
||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import com.volmit.iris.util.scheduling.Looper;
|
import com.volmit.iris.util.scheduling.Looper;
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
import lombok.Synchronized;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.server.PluginDisableEvent;
|
|
||||||
import org.bukkit.event.server.ServerLoadEvent;
|
|
||||||
import org.bukkit.event.world.WorldLoadEvent;
|
import org.bukkit.event.world.WorldLoadEvent;
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
import org.checkerframework.checker.units.qual.A;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class IrisEngineSVC implements IrisService {
|
public class IrisEngineSVC implements IrisService {
|
||||||
public static IrisEngineSVC instance;
|
private final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||||
public boolean isServerShuttingDown = false;
|
private final AtomicInteger tectonicPlates = new AtomicInteger();
|
||||||
public boolean isServerLoaded = false;
|
private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
|
||||||
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
private final AtomicInteger trimmerAlive = new AtomicInteger();
|
||||||
private ReentrantLock lastUseLock;
|
private final AtomicInteger unloaderAlive = new AtomicInteger();
|
||||||
private KMap<World, Long> lastUse;
|
private final AtomicInteger totalWorlds = new AtomicInteger();
|
||||||
private List<World> IrisWorlds;
|
private final AtomicDouble maxIdleDuration = new AtomicDouble();
|
||||||
private Looper cacheTicker;
|
private final AtomicDouble minIdleDuration = new AtomicDouble();
|
||||||
private Looper trimTicker;
|
private final AtomicLong loadedChunks = new AtomicLong();
|
||||||
private Looper unloadTicker;
|
private final KMap<World, Registered> worlds = new KMap<>();
|
||||||
|
private ScheduledExecutorService service;
|
||||||
private Looper updateTicker;
|
private Looper updateTicker;
|
||||||
private PrecisionStopwatch trimAlive;
|
|
||||||
private PrecisionStopwatch unloadAlive;
|
|
||||||
public PrecisionStopwatch trimActiveAlive;
|
|
||||||
public PrecisionStopwatch unloadActiveAlive;
|
|
||||||
private AtomicInteger TotalTectonicPlates;
|
|
||||||
private AtomicInteger TotalQueuedTectonicPlates;
|
|
||||||
private AtomicInteger TotalNotQueuedTectonicPlates;
|
|
||||||
private AtomicBoolean IsUnloadAlive;
|
|
||||||
private AtomicBoolean IsTrimAlive;
|
|
||||||
ChronoLatch cl;
|
|
||||||
|
|
||||||
public List<World> corruptedIrisWorlds = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
this.cl = new ChronoLatch(5000);
|
var settings = IrisSettings.get().getPerformance();
|
||||||
lastUse = new KMap<>();
|
var engine = settings.getEngineSVC();
|
||||||
lastUseLock = new ReentrantLock();
|
service = Executors.newScheduledThreadPool(0,
|
||||||
IrisWorlds = new ArrayList<>();
|
(engine.isUseVirtualThreads()
|
||||||
IsUnloadAlive = new AtomicBoolean(true);
|
? Thread.ofVirtual()
|
||||||
IsTrimAlive = new AtomicBoolean(true);
|
: Thread.ofPlatform().priority(engine.getPriority()))
|
||||||
trimActiveAlive = new PrecisionStopwatch();
|
.name("Iris EngineSVC-", 0)
|
||||||
unloadActiveAlive = new PrecisionStopwatch();
|
.factory());
|
||||||
trimAlive = new PrecisionStopwatch();
|
tectonicLimit.set(settings.getTectonicPlateSize());
|
||||||
unloadAlive = new PrecisionStopwatch();
|
Bukkit.getWorlds().forEach(this::add);
|
||||||
TotalTectonicPlates = new AtomicInteger();
|
setup();
|
||||||
TotalQueuedTectonicPlates = new AtomicInteger();
|
|
||||||
TotalNotQueuedTectonicPlates = new AtomicInteger();
|
|
||||||
tectonicLimit.set(2);
|
|
||||||
long t = getHardware.getProcessMemory();
|
|
||||||
while (t > 200) {
|
|
||||||
tectonicLimit.getAndAdd(1);
|
|
||||||
t = t - 200;
|
|
||||||
}
|
|
||||||
this.setup();
|
|
||||||
this.TrimLogic();
|
|
||||||
this.UnloadLogic();
|
|
||||||
|
|
||||||
trimAlive.begin();
|
|
||||||
unloadAlive.begin();
|
|
||||||
trimActiveAlive.begin();
|
|
||||||
unloadActiveAlive.begin();
|
|
||||||
|
|
||||||
updateTicker.start();
|
|
||||||
cacheTicker.start();
|
|
||||||
//trimTicker.start();
|
|
||||||
//unloadTicker.start();
|
|
||||||
instance = this;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void engineStatus() {
|
@Override
|
||||||
boolean trimAlive = trimTicker.isAlive();
|
public void onDisable() {
|
||||||
boolean unloadAlive = unloadTicker.isAlive();
|
service.shutdown();
|
||||||
Iris.info("Status:");
|
updateTicker.interrupt();
|
||||||
Iris.info("- Trim: " + trimAlive);
|
worlds.keySet().forEach(this::remove);
|
||||||
Iris.info("- Unload: " + unloadAlive);
|
worlds.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getTectonicLimit() {
|
public void engineStatus(VolmitSender sender) {
|
||||||
return tectonicLimit.get();
|
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "Status:");
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (service.isShutdown() ? "Shutdown" : "Running"));
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Updater: " + C.LIGHT_PURPLE + (updateTicker.isAlive() ? "Running" : "Stopped"));
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Trimmers: " + C.LIGHT_PURPLE + trimmerAlive.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Unloaders: " + C.LIGHT_PURPLE + unloaderAlive.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:");
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Limit: " + C.LIGHT_PURPLE + tectonicLimit.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Total: " + C.LIGHT_PURPLE + tectonicPlates.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + queuedTectonicPlates.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2));
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2));
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "Other:");
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + totalWorlds.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get());
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "- Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
||||||
|
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onWorldUnload(WorldUnloadEvent event) {
|
public void onWorldUnload(WorldUnloadEvent event) {
|
||||||
updateWorlds();
|
remove(event.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onWorldLoad(WorldLoadEvent event) {
|
public void onWorldLoad(WorldLoadEvent event) {
|
||||||
updateWorlds();
|
add(event.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
private void remove(World world) {
|
||||||
public void onServerBoot(ServerLoadEvent event) {
|
var entry = worlds.remove(world);
|
||||||
isServerLoaded = true;
|
if (entry == null) return;
|
||||||
|
entry.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
private void add(World world) {
|
||||||
public void onPluginDisable(PluginDisableEvent event) {
|
var access = IrisToolbelt.access(world);
|
||||||
if (event.getPlugin().equals(Iris.instance)) {
|
if (access == null) return;
|
||||||
isServerShuttingDown = true;
|
worlds.put(world, new Registered(world.getName(), access));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateWorlds() {
|
private synchronized void setup() {
|
||||||
for (World world : Bukkit.getWorlds()) {
|
if (updateTicker != null && updateTicker.isAlive())
|
||||||
try {
|
return;
|
||||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
|
||||||
IrisWorlds.add(world);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// no
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setup() {
|
|
||||||
cacheTicker = new Looper() {
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
lastUseLock.lock();
|
|
||||||
try {
|
|
||||||
for (World key : new ArrayList<>(lastUse.keySet())) {
|
|
||||||
Long last = lastUse.get(key);
|
|
||||||
if (last == null)
|
|
||||||
continue;
|
|
||||||
if (now - last > 60000) {
|
|
||||||
lastUse.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lastUseLock.unlock();
|
|
||||||
}
|
|
||||||
return 1000;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTicker = new Looper() {
|
updateTicker = new Looper() {
|
||||||
@Override
|
@Override
|
||||||
protected long loop() {
|
protected long loop() {
|
||||||
try {
|
try {
|
||||||
TotalQueuedTectonicPlates.set(0);
|
queuedTectonicPlates.set(0);
|
||||||
TotalNotQueuedTectonicPlates.set(0);
|
tectonicPlates.set(0);
|
||||||
TotalTectonicPlates.set(0);
|
loadedChunks.set(0);
|
||||||
for (World world : IrisWorlds) {
|
unloaderAlive.set(0);
|
||||||
Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine();
|
trimmerAlive.set(0);
|
||||||
TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload());
|
totalWorlds.set(0);
|
||||||
TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions());
|
|
||||||
TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
|
||||||
}
|
|
||||||
if (!isServerShuttingDown && isServerLoaded) {
|
|
||||||
if (!trimTicker.isAlive()) {
|
|
||||||
Iris.info(C.RED + "TrimTicker found dead! Booting it up!");
|
|
||||||
try {
|
|
||||||
TrimLogic();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.error("What happened?");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!unloadTicker.isAlive()) {
|
double maxDuration = Long.MIN_VALUE;
|
||||||
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!");
|
double minDuration = Long.MAX_VALUE;
|
||||||
try {
|
for (var entry : worlds.entrySet()) {
|
||||||
UnloadLogic();
|
var registered = entry.getValue();
|
||||||
} catch (Exception e) {
|
if (registered.closed) continue;
|
||||||
Iris.error("What happened?");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
totalWorlds.incrementAndGet();
|
||||||
return -1;
|
unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0);
|
||||||
|
trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0);
|
||||||
|
|
||||||
|
var engine = registered.getEngine();
|
||||||
|
if (engine == null) continue;
|
||||||
|
|
||||||
|
queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount());
|
||||||
|
tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
||||||
|
loadedChunks.addAndGet(entry.getKey().getLoadedChunks().length);
|
||||||
|
|
||||||
|
double duration = engine.getMantle().getAdjustedIdleDuration();
|
||||||
|
if (duration > maxDuration) maxDuration = duration;
|
||||||
|
if (duration < minDuration) minDuration = duration;
|
||||||
|
}
|
||||||
|
maxIdleDuration.set(maxDuration);
|
||||||
|
minIdleDuration.set(minDuration);
|
||||||
|
|
||||||
|
worlds.values().forEach(Registered::update);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
updateTicker.start();
|
||||||
}
|
}
|
||||||
public void TrimLogic() {
|
|
||||||
if (trimTicker == null || !trimTicker.isAlive()) {
|
|
||||||
trimTicker = new Looper() {
|
|
||||||
private final Supplier<Engine> supplier = createSupplier();
|
|
||||||
|
|
||||||
@Override
|
private final class Registered {
|
||||||
protected long loop() {
|
private final String name;
|
||||||
long start = System.currentTimeMillis();
|
private final PlatformChunkGenerator access;
|
||||||
trimAlive.reset();
|
private transient ScheduledFuture<?> trimmer;
|
||||||
|
private transient ScheduledFuture<?> unloader;
|
||||||
|
private transient boolean closed;
|
||||||
|
|
||||||
|
private Registered(String name, PlatformChunkGenerator access) {
|
||||||
|
this.name = name;
|
||||||
|
this.access = access;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean unloaderAlive() {
|
||||||
|
return unloader != null && !unloader.isDone() && !unloader.isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean trimmerAlive() {
|
||||||
|
return trimmer != null && !trimmer.isDone() && !trimmer.isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private void update() {
|
||||||
|
if (closed || service == null || service.isShutdown())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) {
|
||||||
|
trimmer = service.scheduleAtFixedRate(() -> {
|
||||||
|
Engine engine = getEngine();
|
||||||
|
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||||
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Engine engine = supplier.get();
|
engine.getMantle().trim(tectonicLimit.get() / worlds.size());
|
||||||
if (engine != null) {
|
} catch (Throwable e) {
|
||||||
engine.getMantle().trim(tectonicLimit.get() / lastUse.size());
|
Iris.reportError(e);
|
||||||
|
Iris.error("EngineSVC: Failed to trim for " + name);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
||||||
|
unloader = service.scheduleAtFixedRate(() -> {
|
||||||
|
Engine engine = getEngine();
|
||||||
|
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
long unloadStart = System.currentTimeMillis();
|
||||||
|
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / worlds.size());
|
||||||
|
if (count > 0) {
|
||||||
|
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
Iris.info(C.RED + "EngineSVC: Failed to trim.");
|
Iris.error("EngineSVC: Failed to unload for " + name);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
||||||
int size = lastUse.size();
|
|
||||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
|
||||||
if (time <= 0)
|
|
||||||
return 0;
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
trimTicker.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void UnloadLogic() {
|
|
||||||
if (unloadTicker == null || !unloadTicker.isAlive()) {
|
|
||||||
unloadTicker = new Looper() {
|
|
||||||
private final Supplier<Engine> supplier = createSupplier();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
unloadAlive.reset();
|
|
||||||
try {
|
|
||||||
Engine engine = supplier.get();
|
|
||||||
if (engine != null) {
|
|
||||||
long unloadStart = System.currentTimeMillis();
|
|
||||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size());
|
|
||||||
if (count > 0) {
|
|
||||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
Iris.info(C.RED + "EngineSVC: Failed to unload.");
|
|
||||||
e.printStackTrace();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = lastUse.size();
|
|
||||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
|
||||||
if (time <= 0)
|
|
||||||
return 0;
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
unloadTicker.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Supplier<Engine> createSupplier() {
|
|
||||||
AtomicInteger i = new AtomicInteger();
|
|
||||||
return () -> {
|
|
||||||
List<World> worlds = Bukkit.getWorlds();
|
|
||||||
if (i.get() >= worlds.size()) {
|
|
||||||
i.set(0);
|
|
||||||
}
|
}
|
||||||
try {
|
}
|
||||||
for (int j = 0; j < worlds.size(); j++) {
|
|
||||||
World world = worlds.get(i.getAndIncrement());
|
|
||||||
PlatformChunkGenerator generator = IrisToolbelt.access(world);
|
|
||||||
if (i.get() >= worlds.size()) {
|
|
||||||
i.set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generator != null) {
|
@Synchronized
|
||||||
Engine engine = generator.getEngine();
|
private void close() {
|
||||||
boolean closed = engine.getMantle().getData().isClosed();
|
if (closed) return;
|
||||||
if (engine != null && !engine.isStudio() && !closed) {
|
closed = true;
|
||||||
lastUseLock.lock();
|
|
||||||
lastUse.put(world, System.currentTimeMillis());
|
if (trimmer != null) {
|
||||||
lastUseLock.unlock();
|
trimmer.cancel(false);
|
||||||
return engine;
|
trimmer = null;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.info(C.RED + "EngineSVC: Failed to create supplier.");
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.reportError(e);
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (unloader != null) {
|
||||||
public void onDisable() {
|
unloader.cancel(false);
|
||||||
cacheTicker.interrupt();
|
unloader = null;
|
||||||
trimTicker.interrupt();
|
}
|
||||||
unloadTicker.interrupt();
|
}
|
||||||
lastUse.clear();
|
|
||||||
|
@Nullable
|
||||||
|
private Engine getEngine() {
|
||||||
|
if (closed) return null;
|
||||||
|
return access.getEngine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,625 +0,0 @@
|
|||||||
package com.volmit.iris.core.tools;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import oshi.SystemInfo;
|
|
||||||
import oshi.hardware.CentralProcessor;
|
|
||||||
import oshi.hardware.GlobalMemory;
|
|
||||||
import oshi.hardware.HWDiskStore;
|
|
||||||
import oshi.software.os.OperatingSystem;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import java.lang.management.MemoryMXBean;
|
|
||||||
import java.lang.management.MemoryUsage;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import static com.google.common.math.LongMath.isPrime;
|
|
||||||
import static com.volmit.iris.util.misc.getHardware.getCPUModel;
|
|
||||||
public class IrisBenchmarking {
|
|
||||||
static String ServerOS;
|
|
||||||
static String filePath = "benchmark.dat";
|
|
||||||
static double avgWriteSpeedMBps;
|
|
||||||
static double avgReadSpeedMBps;
|
|
||||||
static double highestWriteSpeedMBps;
|
|
||||||
static double highestReadSpeedMBps;
|
|
||||||
static double lowestWriteSpeedMBps;
|
|
||||||
static double lowestReadSpeedMBps;
|
|
||||||
static double calculateIntegerMath;
|
|
||||||
static double calculateFloatingPoint;
|
|
||||||
static double calculatePrimeNumbers;
|
|
||||||
static double calculateStringSorting;
|
|
||||||
static double calculateDataEncryption;
|
|
||||||
static double calculateDataCompression;
|
|
||||||
static String currentRunning = "None";
|
|
||||||
static int BenchmarksCompleted = 0;
|
|
||||||
static int BenchmarksTotal = 7;
|
|
||||||
static int totalTasks = 10;
|
|
||||||
static int currentTasks = 0;
|
|
||||||
static double WindowsCPUCompression;
|
|
||||||
static double WindowsCPUEncryption;
|
|
||||||
static double WindowsCPUCSHA1;
|
|
||||||
static double elapsedTimeNs;
|
|
||||||
static boolean Winsat = false;
|
|
||||||
static boolean WindowsDiskSpeed = false;
|
|
||||||
public static boolean inProgress = false;
|
|
||||||
static double startTime;
|
|
||||||
// Good enough for now. . .
|
|
||||||
|
|
||||||
public static void runBenchmark() throws InterruptedException {
|
|
||||||
inProgress = true;
|
|
||||||
getServerOS();
|
|
||||||
deleteTestFile(filePath);
|
|
||||||
AtomicReference<Double> doneCalculateDiskSpeed = new AtomicReference<>((double) 0);
|
|
||||||
startBenchmarkTimer();
|
|
||||||
Iris.info("Benchmark Started!");
|
|
||||||
Iris.warn("Although it may seem momentarily paused, it's actively processing.");
|
|
||||||
BenchmarksCompleted = 0;
|
|
||||||
|
|
||||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
|
||||||
currentRunning = "calculateDiskSpeed";
|
|
||||||
progressBar();
|
|
||||||
if (ServerOS.contains("Windows") && isRunningAsAdmin()) {
|
|
||||||
WindowsDiskSpeed = true;
|
|
||||||
WindowsDiskSpeedTest();
|
|
||||||
} else {
|
|
||||||
warningFallback();
|
|
||||||
try {
|
|
||||||
Thread.sleep(10);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
doneCalculateDiskSpeed.set(roundToTwoDecimalPlaces(calculateDiskSpeed()));
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "WindowsCpuSpeedTest";
|
|
||||||
progressBar();
|
|
||||||
if (ServerOS.contains("Windows") && isRunningAsAdmin()) {
|
|
||||||
Winsat = true;
|
|
||||||
WindowsCpuSpeedTest();
|
|
||||||
} else {
|
|
||||||
Iris.info("Skipping:" + C.BLUE + " Windows System Assessment Tool Benchmarks");
|
|
||||||
if (!ServerOS.contains("Windows")) {
|
|
||||||
Iris.info("Required Software:" + C.BLUE + " Windows");
|
|
||||||
BenchmarksTotal = 6;
|
|
||||||
}
|
|
||||||
if (!isRunningAsAdmin()) {
|
|
||||||
Iris.info(C.RED + "ERROR: " + C.DARK_RED + "Elevated privileges missing");
|
|
||||||
BenchmarksTotal = 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "calculateIntegerMath";
|
|
||||||
progressBar();
|
|
||||||
calculateIntegerMath = roundToTwoDecimalPlaces(calculateIntegerMath());
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "calculateFloatingPoint";
|
|
||||||
progressBar();
|
|
||||||
calculateFloatingPoint = roundToTwoDecimalPlaces(calculateFloatingPoint());
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "calculateStringSorting";
|
|
||||||
progressBar();
|
|
||||||
calculateStringSorting = roundToTwoDecimalPlaces(calculateStringSorting());
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "calculatePrimeNumbers";
|
|
||||||
progressBar();
|
|
||||||
calculatePrimeNumbers = roundToTwoDecimalPlaces(calculatePrimeNumbers());
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "calculateDataEncryption";
|
|
||||||
progressBar();
|
|
||||||
calculateDataEncryption = roundToTwoDecimalPlaces(calculateDataEncryption());
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}).thenRun(() -> {
|
|
||||||
currentRunning = "calculateDataCompression";
|
|
||||||
progressBar();
|
|
||||||
calculateDataCompression = roundToTwoDecimalPlaces(calculateDataCompression());
|
|
||||||
BenchmarksCompleted++;
|
|
||||||
}).thenRun(() -> {
|
|
||||||
elapsedTimeNs = stopBenchmarkTimer();
|
|
||||||
results();
|
|
||||||
inProgress = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
future.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void progressBar() {
|
|
||||||
Iris.info("-----------------------------------------------------");
|
|
||||||
Iris.info("Currently Running: " + C.BLUE + currentRunning);
|
|
||||||
// Iris.info("Tasks: " + "Current Tasks: " + C.BLUE + currentTasks + C.WHITE + " / " + "Total Tasks: " + C.BLUE + totalTasks);
|
|
||||||
Iris.info("Benchmarks Completed: " + C.BLUE + BenchmarksCompleted + C.WHITE + " / " + "Total: " + C.BLUE + BenchmarksTotal);
|
|
||||||
Iris.info("-----------------------------------------------------");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void results() {
|
|
||||||
|
|
||||||
SystemInfo systemInfo = new SystemInfo();
|
|
||||||
GlobalMemory globalMemory = systemInfo.getHardware().getMemory();
|
|
||||||
long totalMemoryMB = globalMemory.getTotal() / (1024 * 1024);
|
|
||||||
long availableMemoryMB = globalMemory.getAvailable() / (1024 * 1024);
|
|
||||||
long totalPageSize = globalMemory.getPageSize() / (1024 * 1024);
|
|
||||||
long usedMemoryMB = totalMemoryMB - availableMemoryMB;
|
|
||||||
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
|
|
||||||
|
|
||||||
Iris.info("OS: " + ServerOS);
|
|
||||||
if (!isRunningAsAdmin() || !ServerOS.contains("Windows")) {
|
|
||||||
Iris.info(C.GOLD + "For the full results use Windows + Admin Rights..");
|
|
||||||
}
|
|
||||||
Iris.info("CPU Model: " + getCPUModel());
|
|
||||||
Iris.info("CPU Score: " + "WIP");
|
|
||||||
Iris.info("- Integer Math: " + calculateIntegerMath + " MOps/Sec");
|
|
||||||
Iris.info("- Floating Point Math: " + calculateFloatingPoint + " MOps/Sec");
|
|
||||||
Iris.info("- Find Prime Numbers: " + calculatePrimeNumbers + " Primes/Sec");
|
|
||||||
Iris.info("- Random String Sorting: " + calculateStringSorting + " Thousand Strings/Sec");
|
|
||||||
Iris.info("- Data Encryption: " + formatDouble(calculateDataEncryption) + " MBytes/Sec");
|
|
||||||
Iris.info("- Data Compression: " + formatDouble(calculateDataCompression) + " MBytes/Sec");
|
|
||||||
|
|
||||||
if (WindowsDiskSpeed) {
|
|
||||||
//Iris.info("Disk Model: " + getDiskModel());
|
|
||||||
Iris.info(C.BLUE + "- Running with Windows System Assessment Tool");
|
|
||||||
Iris.info("- Sequential 64.0 Write: " + C.BLUE + formatDouble(avgWriteSpeedMBps) + " Mbps");
|
|
||||||
Iris.info("- Sequential 64.0 Read: " + C.BLUE + formatDouble(avgReadSpeedMBps) + " Mbps");
|
|
||||||
} else {
|
|
||||||
// Iris.info("Disk Model: " + getDiskModel());
|
|
||||||
Iris.info(C.GREEN + "- Running in Native Mode");
|
|
||||||
Iris.info("- Average Write Speed: " + C.GREEN + formatDouble(avgWriteSpeedMBps) + " Mbps");
|
|
||||||
Iris.info("- Average Read Speed: " + C.GREEN + formatDouble(avgReadSpeedMBps) + " Mbps");
|
|
||||||
Iris.info("- Highest Write Speed: " + formatDouble(highestWriteSpeedMBps) + " Mbps");
|
|
||||||
Iris.info("- Highest Read Speed: " + formatDouble(highestReadSpeedMBps) + " Mbps");
|
|
||||||
Iris.info("- Lowest Write Speed: " + formatDouble(lowestWriteSpeedMBps) + " Mbps");
|
|
||||||
Iris.info("- Lowest Read Speed: " + formatDouble(lowestReadSpeedMBps) + " Mbps");
|
|
||||||
}
|
|
||||||
Iris.info("Ram Usage: ");
|
|
||||||
Iris.info("- Total Ram: " + totalMemoryMB + " MB");
|
|
||||||
Iris.info("- Used Ram: " + usedMemoryMB + " MB");
|
|
||||||
Iris.info("- Total Process Ram: " + C.BLUE + getMaxMemoryUsage() + " MB");
|
|
||||||
Iris.info("- Total Paging Size: " + totalPageSize + " MB");
|
|
||||||
if (Winsat) {
|
|
||||||
Iris.info(C.BLUE + "Windows System Assessment Tool: ");
|
|
||||||
Iris.info("- CPU LZW Compression:" + C.BLUE + formatDouble(WindowsCPUCompression) + " MB/s");
|
|
||||||
Iris.info("- CPU AES256 Encryption: " + C.BLUE + formatDouble(WindowsCPUEncryption) + " MB/s");
|
|
||||||
Iris.info("- CPU SHA1 Hash: " + C.BLUE + formatDouble(WindowsCPUCSHA1) + " MB/s");
|
|
||||||
Iris.info("Duration: " + roundToTwoDecimalPlaces(elapsedTimeNs) + " Seconds");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getMaxMemoryUsage() {
|
|
||||||
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
|
|
||||||
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
|
|
||||||
MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
|
|
||||||
long maxHeapMemory = heapMemoryUsage.getMax();
|
|
||||||
long maxNonHeapMemory = nonHeapMemoryUsage.getMax();
|
|
||||||
long maxMemoryUsageMB = (maxHeapMemory + maxNonHeapMemory) / (1024 * 1024);
|
|
||||||
return maxMemoryUsageMB;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void getServerOS() {
|
|
||||||
SystemInfo systemInfo = new SystemInfo();
|
|
||||||
OperatingSystem os = systemInfo.getOperatingSystem();
|
|
||||||
ServerOS = os.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isRunningAsAdmin() {
|
|
||||||
if (ServerOS.contains("Windows")) {
|
|
||||||
try {
|
|
||||||
Process process = Runtime.getRuntime().exec("winsat disk");
|
|
||||||
process.waitFor();
|
|
||||||
return process.exitValue() == 0;
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
// Hmm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void warningFallback() {
|
|
||||||
Iris.info(C.RED + "Using the " + C.DARK_RED + "FALLBACK" + C.RED + " method due to compatibility issues. ");
|
|
||||||
Iris.info(C.RED + "Please note that this may result in less accurate results.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatDouble(double value) {
|
|
||||||
return String.format("%.2f", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void startBenchmarkTimer() {
|
|
||||||
startTime = System.nanoTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double stopBenchmarkTimer() {
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
return (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double calculateIntegerMath() {
|
|
||||||
final int numIterations = 1_000_000_000;
|
|
||||||
final int numRuns = 30;
|
|
||||||
double totalMopsPerSec = 0;
|
|
||||||
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < numIterations; i++) {
|
|
||||||
result += i * 2;
|
|
||||||
result -= i / 2;
|
|
||||||
result ^= i;
|
|
||||||
result <<= 1;
|
|
||||||
result >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0;
|
|
||||||
|
|
||||||
totalMopsPerSec += mopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
double averageMopsPerSec = totalMopsPerSec / numRuns;
|
|
||||||
return averageMopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double calculateFloatingPoint() {
|
|
||||||
long numIterations = 85_000_000;
|
|
||||||
int numRuns = 30;
|
|
||||||
double totalMopsPerSec = 0;
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
double result = 0;
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
|
|
||||||
for (int i = 0; i < numIterations; i++) {
|
|
||||||
result += Math.sqrt(i) * Math.sin(i) / (i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0;
|
|
||||||
|
|
||||||
totalMopsPerSec += mopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
double averageMopsPerSec = totalMopsPerSec / numRuns;
|
|
||||||
return averageMopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double calculatePrimeNumbers() {
|
|
||||||
int primeCount;
|
|
||||||
long numIterations = 1_000_000;
|
|
||||||
int numRuns = 30;
|
|
||||||
double totalMopsPerSec = 0;
|
|
||||||
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
primeCount = 0;
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
|
|
||||||
for (int num = 2; primeCount < numIterations; num++) {
|
|
||||||
if (isPrime(num)) {
|
|
||||||
primeCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
double mopsPerSec = (primeCount / elapsedSeconds) / 1_000_000.0;
|
|
||||||
|
|
||||||
totalMopsPerSec += mopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
double averageMopsPerSec = totalMopsPerSec / numRuns;
|
|
||||||
return averageMopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double calculateStringSorting() {
|
|
||||||
int stringCount = 1_000_000;
|
|
||||||
int stringLength = 100;
|
|
||||||
int numRuns = 30;
|
|
||||||
double totalMopsPerSec = 0;
|
|
||||||
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
List<String> randomStrings = generateRandomStrings(stringCount, stringLength);
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
randomStrings.sort(String::compareTo);
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
double mopsPerSec = (stringCount / elapsedSeconds) / 1_000.0;
|
|
||||||
|
|
||||||
totalMopsPerSec += mopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
double averageMopsPerSec = totalMopsPerSec / numRuns;
|
|
||||||
return averageMopsPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double calculateDataEncryption() {
|
|
||||||
int dataSizeMB = 100;
|
|
||||||
byte[] dataToEncrypt = generateRandomData(dataSizeMB * 1024 * 1024);
|
|
||||||
int numRuns = 20;
|
|
||||||
double totalMBytesPerSec = 0;
|
|
||||||
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
byte[] encryptedData = performEncryption(dataToEncrypt, 1);
|
|
||||||
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
double mbytesPerSec = (dataToEncrypt.length / (1024 * 1024.0)) / elapsedSeconds;
|
|
||||||
|
|
||||||
totalMBytesPerSec += mbytesPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
double averageMBytesPerSec = totalMBytesPerSec / numRuns;
|
|
||||||
return averageMBytesPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] performEncryption(byte[] data, int numRuns) {
|
|
||||||
byte[] key = "MyEncryptionKey".getBytes();
|
|
||||||
byte[] result = Arrays.copyOf(data, data.length);
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
for (int i = 0; i < result.length; i++) {
|
|
||||||
result[i] ^= key[i % key.length];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double calculateDataCompression() {
|
|
||||||
int dataSizeMB = 500;
|
|
||||||
byte[] dataToCompress = generateRandomData(dataSizeMB * 1024 * 1024);
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
byte[] compressedData = performCompression(dataToCompress);
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1e9;
|
|
||||||
double mbytesPerSec = (compressedData.length / (1024.0 * 1024.0)) / elapsedSeconds;
|
|
||||||
|
|
||||||
return mbytesPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] performCompression(byte[] data) {
|
|
||||||
Deflater deflater = new Deflater();
|
|
||||||
deflater.setInput(data);
|
|
||||||
deflater.finish();
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
|
|
||||||
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
while (!deflater.finished()) {
|
|
||||||
int count = deflater.deflate(buffer);
|
|
||||||
outputStream.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
deflater.end();
|
|
||||||
return outputStream.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<String> generateRandomStrings(int count, int length) {
|
|
||||||
SecureRandom random = new SecureRandom();
|
|
||||||
List<String> randomStrings = new ArrayList<>();
|
|
||||||
|
|
||||||
IntStream.range(0, count).forEach(i -> {
|
|
||||||
byte[] bytes = new byte[length];
|
|
||||||
random.nextBytes(bytes);
|
|
||||||
randomStrings.add(Base64.getEncoder().encodeToString(bytes));
|
|
||||||
});
|
|
||||||
return randomStrings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] generateRandomData(int size) {
|
|
||||||
SecureRandom random = new SecureRandom();
|
|
||||||
byte[] data = new byte[size];
|
|
||||||
random.nextBytes(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double roundToTwoDecimalPlaces(double value) {
|
|
||||||
return Double.parseDouble(String.format("%.2f", value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double calculateCPUScore(long elapsedTimeNs) {
|
|
||||||
return 1.0 / (elapsedTimeNs / 1_000_000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double calculateDiskSpeed() {
|
|
||||||
int numRuns = 10;
|
|
||||||
int fileSizeMB = 1000;
|
|
||||||
|
|
||||||
double[] writeSpeeds = new double[numRuns];
|
|
||||||
double[] readSpeeds = new double[numRuns];
|
|
||||||
|
|
||||||
for (int run = 0; run < numRuns; run++) {
|
|
||||||
long writeStartTime = System.nanoTime();
|
|
||||||
deleteTestFile(filePath);
|
|
||||||
createTestFile(filePath, fileSizeMB);
|
|
||||||
long writeEndTime = System.nanoTime();
|
|
||||||
|
|
||||||
long readStartTime = System.nanoTime();
|
|
||||||
readTestFile(filePath);
|
|
||||||
long readEndTime = System.nanoTime();
|
|
||||||
|
|
||||||
double writeSpeed = calculateDiskSpeedMBps(fileSizeMB, writeStartTime, writeEndTime);
|
|
||||||
double readSpeed = calculateDiskSpeedMBps(fileSizeMB, readStartTime, readEndTime);
|
|
||||||
|
|
||||||
writeSpeeds[run] = writeSpeed;
|
|
||||||
readSpeeds[run] = readSpeed;
|
|
||||||
|
|
||||||
if (run == 0) {
|
|
||||||
lowestWriteSpeedMBps = writeSpeed;
|
|
||||||
highestWriteSpeedMBps = writeSpeed;
|
|
||||||
lowestReadSpeedMBps = readSpeed;
|
|
||||||
highestReadSpeedMBps = readSpeed;
|
|
||||||
} else {
|
|
||||||
if (writeSpeed < lowestWriteSpeedMBps) {
|
|
||||||
lowestWriteSpeedMBps = writeSpeed;
|
|
||||||
}
|
|
||||||
if (writeSpeed > highestWriteSpeedMBps) {
|
|
||||||
highestWriteSpeedMBps = writeSpeed;
|
|
||||||
}
|
|
||||||
if (readSpeed < lowestReadSpeedMBps) {
|
|
||||||
lowestReadSpeedMBps = readSpeed;
|
|
||||||
}
|
|
||||||
if (readSpeed > highestReadSpeedMBps) {
|
|
||||||
highestReadSpeedMBps = readSpeed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
avgWriteSpeedMBps = calculateAverage(writeSpeeds);
|
|
||||||
avgReadSpeedMBps = calculateAverage(readSpeeds);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void createTestFile(String filePath, int fileSizeMB) {
|
|
||||||
try {
|
|
||||||
File file = new File(filePath);
|
|
||||||
byte[] data = new byte[1024 * 1024];
|
|
||||||
Arrays.fill(data, (byte) 0);
|
|
||||||
FileOutputStream fos = new FileOutputStream(file);
|
|
||||||
for (int i = 0; i < fileSizeMB; i++) {
|
|
||||||
fos.write(data);
|
|
||||||
}
|
|
||||||
fos.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void readTestFile(String filePath) {
|
|
||||||
try {
|
|
||||||
File file = new File(filePath);
|
|
||||||
FileInputStream fis = new FileInputStream(file);
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
while (fis.read(buffer) != -1) {
|
|
||||||
}
|
|
||||||
fis.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void deleteTestFile(String filePath) {
|
|
||||||
File file = new File(filePath);
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double calculateDiskSpeedMBps(int fileSizeMB, long startTime, long endTime) {
|
|
||||||
double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
|
|
||||||
double writeSpeed = (fileSizeMB / elapsedSeconds);
|
|
||||||
return writeSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double calculateAverage(double[] values) {
|
|
||||||
double sum = 0;
|
|
||||||
for (double value : values) {
|
|
||||||
sum += value;
|
|
||||||
}
|
|
||||||
return sum / values.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WindowsDiskSpeedTest() {
|
|
||||||
try {
|
|
||||||
String command = "winsat disk";
|
|
||||||
Process process = Runtime.getRuntime().exec(command);
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
|
||||||
String line;
|
|
||||||
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
Iris.debug(line);
|
|
||||||
|
|
||||||
if (line.contains("Disk Sequential 64.0 Read")) {
|
|
||||||
avgReadSpeedMBps = extractSpeed(line);
|
|
||||||
} else if (line.contains("Disk Sequential 64.0 Write")) {
|
|
||||||
avgWriteSpeedMBps = extractSpeed(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process.waitFor();
|
|
||||||
process.destroy();
|
|
||||||
|
|
||||||
Iris.debug("Sequential Read Speed: " + avgReadSpeedMBps + " MB/s");
|
|
||||||
Iris.debug("Sequential Write Speed: " + avgWriteSpeedMBps + " MB/s");
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double extractSpeed(String line) {
|
|
||||||
String[] tokens = line.split("\\s+");
|
|
||||||
for (int i = 0; i < tokens.length; i++) {
|
|
||||||
if (tokens[i].endsWith("MB/s") && i > 0) {
|
|
||||||
try {
|
|
||||||
return Double.parseDouble(tokens[i - 1]);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WindowsCpuSpeedTest() {
|
|
||||||
try {
|
|
||||||
String command = "winsat cpuformal";
|
|
||||||
Process process = Runtime.getRuntime().exec(command);
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
|
||||||
String line;
|
|
||||||
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
Iris.debug(line);
|
|
||||||
|
|
||||||
if (line.contains("CPU AES256 Encryption")) {
|
|
||||||
WindowsCPUEncryption = extractCpuInfo(line);
|
|
||||||
}
|
|
||||||
if (line.contains("CPU LZW Compression")) {
|
|
||||||
WindowsCPUCompression = extractCpuInfo(line);
|
|
||||||
}
|
|
||||||
if (line.contains("CPU SHA1 Hash")) {
|
|
||||||
WindowsCPUCSHA1 = extractCpuInfo(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process.waitFor();
|
|
||||||
process.destroy();
|
|
||||||
|
|
||||||
Iris.debug("Winsat Encryption: " + WindowsCPUEncryption + " MB/s");
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double extractCpuInfo(String line) {
|
|
||||||
String[] tokens = line.split("\\s+");
|
|
||||||
for (int i = 0; i < tokens.length; i++) {
|
|
||||||
if (tokens[i].endsWith("MB/s") && i > 0) {
|
|
||||||
try {
|
|
||||||
return Double.parseDouble(tokens[i - 1]);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,7 @@ import com.volmit.iris.util.collection.KList;
|
|||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.exceptions.IrisException;
|
import com.volmit.iris.util.exceptions.IrisException;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
|
import com.volmit.iris.util.io.IO;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -17,11 +18,6 @@ import org.bukkit.Bukkit;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.FileVisitResult;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.SimpleFileVisitor;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -50,10 +46,7 @@ public class IrisPackBenchmarking {
|
|||||||
.start(() -> {
|
.start(() -> {
|
||||||
Iris.info("Setting up benchmark environment ");
|
Iris.info("Setting up benchmark environment ");
|
||||||
benchmarkInProgress = true;
|
benchmarkInProgress = true;
|
||||||
File file = new File("benchmark");
|
IO.delete(new File(Bukkit.getWorldContainer(), "benchmark"));
|
||||||
if (file.exists()) {
|
|
||||||
deleteDirectory(file.toPath());
|
|
||||||
}
|
|
||||||
createBenchmark();
|
createBenchmark();
|
||||||
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
|
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
|
||||||
J.sleep(1000);
|
J.sleep(1000);
|
||||||
@@ -72,7 +65,7 @@ public class IrisPackBenchmarking {
|
|||||||
|
|
||||||
public void finishedBenchmark(KList<Integer> cps) {
|
public void finishedBenchmark(KList<Integer> cps) {
|
||||||
try {
|
try {
|
||||||
String time = Form.duration(stopwatch.getMillis());
|
String time = Form.duration((long) stopwatch.getMilliseconds());
|
||||||
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
|
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
|
||||||
Iris.info("-----------------");
|
Iris.info("-----------------");
|
||||||
Iris.info("Results:");
|
Iris.info("Results:");
|
||||||
@@ -83,11 +76,7 @@ public class IrisPackBenchmarking {
|
|||||||
Iris.info(" - Lowest CPS: " + findLowest(cps));
|
Iris.info(" - Lowest CPS: " + findLowest(cps));
|
||||||
Iris.info("-----------------");
|
Iris.info("-----------------");
|
||||||
Iris.info("Creating a report..");
|
Iris.info("Creating a report..");
|
||||||
File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks");
|
File results = Iris.instance.getDataFile("packbenchmarks", dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
||||||
profilers.mkdir();
|
|
||||||
|
|
||||||
File results = new File(profilers, dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
|
||||||
results.getParentFile().mkdirs();
|
|
||||||
KMap<String, Double> metrics = engine.getMetrics().pull();
|
KMap<String, Double> metrics = engine.getMetrics().pull();
|
||||||
try (FileWriter writer = new FileWriter(results)) {
|
try (FileWriter writer = new FileWriter(results)) {
|
||||||
writer.write("-----------------\n");
|
writer.write("-----------------\n");
|
||||||
@@ -146,8 +135,8 @@ public class IrisPackBenchmarking {
|
|||||||
IrisToolbelt.pregenerate(PregenTask
|
IrisToolbelt.pregenerate(PregenTask
|
||||||
.builder()
|
.builder()
|
||||||
.gui(gui)
|
.gui(gui)
|
||||||
.width(radius)
|
.radiusX(radius)
|
||||||
.height(radius)
|
.radiusZ(radius)
|
||||||
.build(), Bukkit.getWorld("benchmark")
|
.build(), Bukkit.getWorld("benchmark")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -178,26 +167,4 @@ public class IrisPackBenchmarking {
|
|||||||
private int findHighest(KList<Integer> list) {
|
private int findHighest(KList<Integer> list) {
|
||||||
return Collections.max(list);
|
return Collections.max(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deleteDirectory(Path dir) {
|
|
||||||
try {
|
|
||||||
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
|
|
||||||
@Override
|
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
|
||||||
Files.delete(file);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
|
||||||
Files.delete(dir);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import com.volmit.iris.core.gui.PregeneratorJob;
|
|||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.pregenerator.PregenTask;
|
import com.volmit.iris.core.pregenerator.PregenTask;
|
||||||
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
|
||||||
|
import com.volmit.iris.core.pregenerator.methods.CachedPregenMethod;
|
||||||
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
|
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
|
||||||
import com.volmit.iris.core.service.StudioSVC;
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
@@ -141,7 +142,18 @@ public class IrisToolbelt {
|
|||||||
* @return the pregenerator job (already started)
|
* @return the pregenerator job (already started)
|
||||||
*/
|
*/
|
||||||
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) {
|
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) {
|
||||||
return new PregeneratorJob(task, method, engine);
|
return pregenerate(task, method, engine, IrisSettings.get().getPregen().useCacheByDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a pregenerator task
|
||||||
|
*
|
||||||
|
* @param task the scheduled task
|
||||||
|
* @param method the method to execute the task
|
||||||
|
* @return the pregenerator job (already started)
|
||||||
|
*/
|
||||||
|
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine, boolean cached) {
|
||||||
|
return new PregeneratorJob(task, cached && engine != null ? new CachedPregenMethod(method, engine.getWorld().name()) : method, engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ public class IrisEngine implements Engine {
|
|||||||
File[] roots = getData().getLoaders()
|
File[] roots = getData().getLoaders()
|
||||||
.values()
|
.values()
|
||||||
.stream()
|
.stream()
|
||||||
.map(ResourceLoader::getRoot)
|
.map(ResourceLoader::getFolderName)
|
||||||
|
.map(n -> new File(getData().getDataFolder(), n))
|
||||||
|
.filter(File::exists)
|
||||||
|
.filter(File::isDirectory)
|
||||||
.toArray(File[]::new);
|
.toArray(File[]::new);
|
||||||
hash32.complete(IO.hashRecursive(roots));
|
hash32.complete(IO.hashRecursive(roots));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
|
|||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -367,7 +368,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
|
|
||||||
private void spawn(IrisPosition pos, IrisEntitySpawn i) {
|
private void spawn(IrisPosition pos, IrisEntitySpawn i) {
|
||||||
IrisSpawner ref = i.getReferenceSpawner();
|
IrisSpawner ref = i.getReferenceSpawner();
|
||||||
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ()))
|
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int s = i.spawn(getEngine(), pos, RNG.r);
|
int s = i.spawn(getEngine(), pos, RNG.r);
|
||||||
@@ -422,9 +423,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
energy += 0.3;
|
var ref = new WeakReference<>(e.getWorld());
|
||||||
fixEnergy();
|
int x = e.getX(), z = e.getZ();
|
||||||
getEngine().cleanupMantleChunk(e.getX(), e.getZ());
|
J.s(() -> {
|
||||||
|
World world = ref.get();
|
||||||
|
if (world == null || !world.isChunkLoaded(x, z))
|
||||||
|
return;
|
||||||
|
energy += 0.3;
|
||||||
|
fixEnergy();
|
||||||
|
getEngine().cleanupMantleChunk(x, z);
|
||||||
|
}, IrisSettings.get().getPerformance().mantleCleanupDelay);
|
||||||
|
|
||||||
if (generated) {
|
if (generated) {
|
||||||
//INMS.get().injectBiomesFromMantle(e, getMantle());
|
//INMS.get().injectBiomesFromMantle(e, getMantle());
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -287,76 +288,79 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var chunk = mantle.getChunk(c);
|
var chunk = mantle.getChunk(c).use();
|
||||||
if (chunk.isFlagged(MantleFlag.ETCHED)) return;
|
try {
|
||||||
chunk.flag(MantleFlag.ETCHED, true);
|
Semaphore semaphore = new Semaphore(3);
|
||||||
|
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
|
||||||
|
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
||||||
|
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
||||||
|
int betterY = y + getWorld().minHeight();
|
||||||
|
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
||||||
|
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
||||||
|
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
||||||
|
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
Semaphore semaphore = new Semaphore(3);
|
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
||||||
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
KMap<Long, Integer> updates = new KMap<>();
|
||||||
int betterY = y + getWorld().minHeight();
|
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
||||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
||||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
int y = yf + getWorld().minHeight();
|
||||||
});
|
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
||||||
})));
|
return;
|
||||||
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
}
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
boolean u = false;
|
||||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
||||||
});
|
u = true;
|
||||||
})));
|
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
||||||
|
u = true;
|
||||||
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
u = true;
|
||||||
KMap<Long, Integer> updates = new KMap<>();
|
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
||||||
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
u = true;
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
||||||
int y = yf + getWorld().minHeight();
|
u = true;
|
||||||
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean u = false;
|
|
||||||
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (u) {
|
|
||||||
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
|
||||||
if (vv != null) {
|
|
||||||
return Math.max(vv, y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return y;
|
if (u) {
|
||||||
|
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
||||||
|
if (vv != null) {
|
||||||
|
return Math.max(vv, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return y;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
||||||
|
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
||||||
|
int y = yf + getWorld().minHeight();
|
||||||
|
if (v != null && v.isUpdate()) {
|
||||||
|
int vx = x & 15;
|
||||||
|
int vz = z & 15;
|
||||||
|
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
||||||
|
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
||||||
|
updateLighting(x, y, z, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
||||||
|
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||||
|
}, RNG.r.i(0, 20))));
|
||||||
});
|
});
|
||||||
|
|
||||||
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
try {
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
semaphore.acquire(3);
|
||||||
int y = yf + getWorld().minHeight();
|
} catch (InterruptedException ignored) {}
|
||||||
if (v != null && v.isUpdate()) {
|
} finally {
|
||||||
int vx = x & 15;
|
chunk.release();
|
||||||
int vz = z & 15;
|
}
|
||||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
|
||||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
|
||||||
updateLighting(x, y, z, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
|
||||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
|
||||||
}, RNG.r.i(0, 20))));
|
|
||||||
|
|
||||||
try {
|
|
||||||
semaphore.acquire(3);
|
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Runnable run(Semaphore semaphore, Runnable runnable) {
|
private static Runnable run(Semaphore semaphore, Runnable runnable) {
|
||||||
@@ -852,6 +856,25 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@BlockCoordinates
|
||||||
|
default Position2 getNearestStronghold(Position2 pos) {
|
||||||
|
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
|
||||||
|
if (p.isEmpty()) return null;
|
||||||
|
|
||||||
|
Position2 pr = null;
|
||||||
|
double d = Double.MAX_VALUE;
|
||||||
|
|
||||||
|
for (Position2 i : p) {
|
||||||
|
double dx = i.distance(pos);
|
||||||
|
if (dx < d) {
|
||||||
|
d = dx;
|
||||||
|
pr = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
default void gotoBiome(IrisBiome biome, Player player, boolean teleport) {
|
default void gotoBiome(IrisBiome biome, Player player, boolean teleport) {
|
||||||
Set<String> regionKeys = getDimension()
|
Set<String> regionKeys = getDimension()
|
||||||
.getAllRegions(this).stream()
|
.getAllRegions(this).stream()
|
||||||
@@ -872,31 +895,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
|
|
||||||
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
|
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
|
||||||
if (s.getLoadKey().equals(getDimension().getStronghold())) {
|
if (s.getLoadKey().equals(getDimension().getStronghold())) {
|
||||||
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
|
Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()));
|
||||||
|
if (pr == null) {
|
||||||
if (p.isEmpty()) {
|
|
||||||
player.sendMessage(C.GOLD + "No strongholds in world.");
|
player.sendMessage(C.GOLD + "No strongholds in world.");
|
||||||
}
|
} else {
|
||||||
|
|
||||||
Position2 px = new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ());
|
|
||||||
Position2 pr = null;
|
|
||||||
double d = Double.MAX_VALUE;
|
|
||||||
|
|
||||||
Iris.debug("Ps: " + p.size());
|
|
||||||
|
|
||||||
for (Position2 i : p) {
|
|
||||||
Iris.debug("- " + i.getX() + " " + i.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Position2 i : p) {
|
|
||||||
double dx = i.distance(px);
|
|
||||||
if (dx < d) {
|
|
||||||
d = dx;
|
|
||||||
pr = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pr != null) {
|
|
||||||
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
|
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
|
||||||
J.s(() -> player.teleport(ll));
|
J.s(() -> player.teleport(ll));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,51 +97,6 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onItemUse(PlayerInteractEvent e) {
|
|
||||||
if (e.getItem() == null || e.getHand() != EquipmentSlot.HAND) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.getAction() == Action.LEFT_CLICK_BLOCK || e.getAction() == Action.LEFT_CLICK_AIR) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.getPlayer().getWorld().equals(getTarget().getWorld().realWorld()) && e.getItem().getType() == Material.ENDER_EYE) {
|
|
||||||
if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.END_PORTAL_FRAME) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KList<Position2> positions = getEngine().getDimension().getStrongholds(getEngine().getSeedManager().getMantle());
|
|
||||||
if (positions.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position2 playerPos = new Position2(e.getPlayer().getLocation().getBlockX(), e.getPlayer().getLocation().getBlockZ());
|
|
||||||
Position2 pr = positions.get(0);
|
|
||||||
double d = pr.distance(playerPos);
|
|
||||||
|
|
||||||
for (Position2 pos : positions) {
|
|
||||||
double distance = pos.distance(playerPos);
|
|
||||||
if (distance < d) {
|
|
||||||
d = distance;
|
|
||||||
pr = pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.getPlayer().getGameMode() != GameMode.CREATIVE) {
|
|
||||||
if (e.getItem().getAmount() > 1) {
|
|
||||||
e.getPlayer().getInventory().getItemInMainHand().setAmount(e.getItem().getAmount() - 1);
|
|
||||||
} else {
|
|
||||||
e.getPlayer().getInventory().setItemInMainHand(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EnderSignal eye = e.getPlayer().getWorld().spawn(e.getPlayer().getLocation().clone().add(0, 0.5F, 0), EnderSignal.class);
|
|
||||||
eye.setTargetLocation(new Location(e.getPlayer().getWorld(), pr.getX(), 40, pr.getZ()));
|
|
||||||
eye.getWorld().playSound(eye, Sound.ENTITY_ENDER_EYE_LAUNCH, 1, 1);
|
|
||||||
Iris.debug("ESignal: " + eye.getTargetLocation().getBlockX() + " " + eye.getTargetLocation().getBlockX());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void on(WorldUnloadEvent e) {
|
public void on(WorldUnloadEvent e) {
|
||||||
if (e.getWorld().equals(getTarget().getWorld().realWorld())) {
|
if (e.getWorld().equals(getTarget().getWorld().realWorld())) {
|
||||||
|
|||||||
@@ -289,23 +289,25 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default void cleanupChunk(int x, int z) {
|
default void cleanupChunk(int x, int z) {
|
||||||
if (!getMantle().hasFlag(x, z, MantleFlag.CLEANED) && isCovered(x, z)) {
|
if (!isCovered(x, z)) return;
|
||||||
getMantle().raiseFlag(x, z, MantleFlag.CLEANED, () -> {
|
MantleChunk chunk = getMantle().getChunk(x, z).use();
|
||||||
getMantle().deleteChunkSlice(x, z, BlockData.class);
|
try {
|
||||||
getMantle().deleteChunkSlice(x, z, String.class);
|
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
|
||||||
getMantle().deleteChunkSlice(x, z, MatterCavern.class);
|
chunk.deleteSlices(BlockData.class);
|
||||||
getMantle().deleteChunkSlice(x, z, MatterFluidBody.class);
|
chunk.deleteSlices(String.class);
|
||||||
|
chunk.deleteSlices(MatterCavern.class);
|
||||||
|
chunk.deleteSlices(MatterFluidBody.class);
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
chunk.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default long getToUnload(){
|
default long getUnloadRegionCount() {
|
||||||
return getMantle().getToUnload().size();
|
return getMantle().getUnloadRegionCount();
|
||||||
}
|
}
|
||||||
default long getNotQueuedLoadedRegions(){
|
|
||||||
return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size();
|
default double getAdjustedIdleDuration() {
|
||||||
}
|
return getMantle().getAdjustedIdleDuration();
|
||||||
default double getTectonicDuration(){
|
|
||||||
return getMantle().getAdjustedIdleDuration().get();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,6 @@ import org.bukkit.block.data.BlockData;
|
|||||||
public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
||||||
private final RNG rng;
|
private final RNG rng;
|
||||||
private final BlockData AIR = Material.CAVE_AIR.createBlockData();
|
private final BlockData AIR = Material.CAVE_AIR.createBlockData();
|
||||||
private final BlockData WATER = Material.WATER.createBlockData();
|
|
||||||
private final BlockData LAVA = Material.LAVA.createBlockData();
|
private final BlockData LAVA = Material.LAVA.createBlockData();
|
||||||
private final IrisDecorantActuator decorant;
|
private final IrisDecorantActuator decorant;
|
||||||
|
|
||||||
@@ -103,7 +102,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (c.isWater()) {
|
if (c.isWater()) {
|
||||||
output.set(rx, yy, rz, WATER);
|
output.set(rx, yy, rz, context.getFluid().get(rx, rz));
|
||||||
} else if (c.isLava()) {
|
} else if (c.isLava()) {
|
||||||
output.set(rx, yy, rz, LAVA);
|
output.set(rx, yy, rz, LAVA);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ package com.volmit.iris.engine.object;
|
|||||||
|
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.nms.container.AutoClosing;
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.misc.ServerProperties;
|
import com.volmit.iris.util.misc.ServerProperties;
|
||||||
|
import lombok.Getter;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.WorldInitEvent;
|
import org.bukkit.event.world.WorldInitEvent;
|
||||||
|
|
||||||
@@ -15,15 +19,12 @@ import java.util.List;
|
|||||||
import static com.volmit.iris.Iris.instance;
|
import static com.volmit.iris.Iris.instance;
|
||||||
|
|
||||||
public class IrisContextInjector implements Listener {
|
public class IrisContextInjector implements Listener {
|
||||||
|
@Getter
|
||||||
|
private static boolean missingDimensionTypes = false;
|
||||||
private AutoClosing autoClosing = null;
|
private AutoClosing autoClosing = null;
|
||||||
private final int totalWorlds;
|
|
||||||
private int worldCounter = 0;
|
|
||||||
|
|
||||||
public IrisContextInjector() {
|
public IrisContextInjector() {
|
||||||
if (!Bukkit.getWorlds().isEmpty()) {
|
if (!Bukkit.getWorlds().isEmpty()) return;
|
||||||
totalWorlds = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String levelName = ServerProperties.LEVEL_NAME;
|
String levelName = ServerProperties.LEVEL_NAME;
|
||||||
List<String> irisWorlds = irisWorlds();
|
List<String> irisWorlds = irisWorlds();
|
||||||
@@ -31,22 +32,20 @@ public class IrisContextInjector implements Listener {
|
|||||||
boolean nether = irisWorlds.contains(levelName + "_nether");
|
boolean nether = irisWorlds.contains(levelName + "_nether");
|
||||||
boolean end = irisWorlds.contains(levelName + "_end");
|
boolean end = irisWorlds.contains(levelName + "_end");
|
||||||
|
|
||||||
int i = 1;
|
if (INMS.get().missingDimensionTypes(overworld, nether, end)) {
|
||||||
if (Bukkit.getAllowNether()) i++;
|
missingDimensionTypes = true;
|
||||||
if (Bukkit.getAllowEnd()) i++;
|
return;
|
||||||
|
|
||||||
if (overworld || nether || end) {
|
|
||||||
var pair = INMS.get().injectUncached(overworld, nether, end);
|
|
||||||
i += pair.getA() - 3;
|
|
||||||
autoClosing = pair.getB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalWorlds = i;
|
if (overworld || nether || end) {
|
||||||
|
autoClosing = INMS.get().injectUncached(overworld, nether, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.registerListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void on(WorldInitEvent event) {
|
public void on(WorldInitEvent event) {
|
||||||
if (++worldCounter < totalWorlds) return;
|
|
||||||
if (autoClosing != null) {
|
if (autoClosing != null) {
|
||||||
autoClosing.close();
|
autoClosing.close();
|
||||||
autoClosing = null;
|
autoClosing = null;
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import com.volmit.iris.util.math.RNG;
|
|||||||
import com.volmit.iris.util.math.Vector3d;
|
import com.volmit.iris.util.math.Vector3d;
|
||||||
import com.volmit.iris.util.matter.MatterMarker;
|
import com.volmit.iris.util.matter.MatterMarker;
|
||||||
import com.volmit.iris.util.matter.slices.MarkerMatter;
|
import com.volmit.iris.util.matter.slices.MarkerMatter;
|
||||||
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -38,9 +37,6 @@ import org.bukkit.Location;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.bukkit.entity.LivingEntity;
|
|
||||||
import org.bukkit.util.BoundingBox;
|
|
||||||
|
|
||||||
@Snippet("entity-spawn")
|
@Snippet("entity-spawn")
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@@ -116,8 +112,8 @@ public class IrisEntitySpawn implements IRare {
|
|||||||
World world = gen.getWorld().realWorld();
|
World world = gen.getWorld().realWorld();
|
||||||
if (spawns > 0) {
|
if (spawns > 0) {
|
||||||
|
|
||||||
if (referenceMarker != null) {
|
if (referenceMarker != null && referenceMarker.shouldExhaust()) {
|
||||||
gen.getMantle().getMantle().remove(c.getX(), c.getY(), c.getZ(), MatterMarker.class);
|
gen.getMantle().getMantle().remove(c.getX(), c.getY() - gen.getWorld().minHeight(), c.getZ(), MatterMarker.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int id = 0; id < spawns; id++) {
|
for (int id = 0; id < spawns; id++) {
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ public class IrisMarker extends IrisRegistrant {
|
|||||||
private boolean emptyAbove = true;
|
private boolean emptyAbove = true;
|
||||||
|
|
||||||
@Desc("If this marker is used, what is the chance it removes itself. For example 25% (0.25) would mean that on average 4 uses will remove a specific marker. Set this below 0 (-1) to never exhaust & set this to 1 or higher to always exhaust on first use.")
|
@Desc("If this marker is used, what is the chance it removes itself. For example 25% (0.25) would mean that on average 4 uses will remove a specific marker. Set this below 0 (-1) to never exhaust & set this to 1 or higher to always exhaust on first use.")
|
||||||
private double exhaustionChance = 0.33;
|
private double exhaustionChance = 0;
|
||||||
|
|
||||||
public boolean shouldExhaust() {
|
public boolean shouldExhaust() {
|
||||||
return RNG.r.chance(exhaustionChance);
|
return exhaustionChance > RNG.r.nextDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package com.volmit.iris.engine.platform;
|
|||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.service.StudioSVC;
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
import com.volmit.iris.engine.IrisEngine;
|
import com.volmit.iris.engine.IrisEngine;
|
||||||
import com.volmit.iris.engine.data.chunk.TerrainChunk;
|
import com.volmit.iris.engine.data.chunk.TerrainChunk;
|
||||||
@@ -39,6 +40,7 @@ import com.volmit.iris.util.io.ReactiveFolder;
|
|||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import com.volmit.iris.util.scheduling.Looper;
|
import com.volmit.iris.util.scheduling.Looper;
|
||||||
|
import io.papermc.lib.PaperLib;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -48,6 +50,7 @@ import org.bukkit.block.data.BlockData;
|
|||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.WorldInitEvent;
|
import org.bukkit.event.world.WorldInitEvent;
|
||||||
import org.bukkit.generator.BiomeProvider;
|
import org.bukkit.generator.BiomeProvider;
|
||||||
@@ -84,12 +87,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
private final boolean studio;
|
private final boolean studio;
|
||||||
private final AtomicInteger a = new AtomicInteger(0);
|
private final AtomicInteger a = new AtomicInteger(0);
|
||||||
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
|
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
|
||||||
private Engine engine;
|
private volatile Engine engine;
|
||||||
private Looper hotloader;
|
private volatile Looper hotloader;
|
||||||
private StudioMode lastMode;
|
private volatile StudioMode lastMode;
|
||||||
private DummyBiomeProvider dummyBiomeProvider;
|
private volatile DummyBiomeProvider dummyBiomeProvider;
|
||||||
@Setter
|
@Setter
|
||||||
private StudioGenerator studioGenerator;
|
private volatile StudioGenerator studioGenerator;
|
||||||
|
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
|
|
||||||
@@ -108,25 +111,13 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
|
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Field getField(Class clazz, String fieldName)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
throws NoSuchFieldException {
|
|
||||||
try {
|
|
||||||
return clazz.getDeclaredField(fieldName);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
Class superClass = clazz.getSuperclass();
|
|
||||||
if (superClass == null) {
|
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
return getField(superClass, fieldName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onWorldInit(WorldInitEvent event) {
|
public void onWorldInit(WorldInitEvent event) {
|
||||||
try {
|
try {
|
||||||
if (initialized || !world.name().equals(event.getWorld().getName()))
|
if (initialized || !world.name().equals(event.getWorld().getName()))
|
||||||
return;
|
return;
|
||||||
|
AutoClosing.closeContext();
|
||||||
|
INMS.get().removeCustomDimensions(event.getWorld());
|
||||||
world.setRawWorldSeed(event.getWorld().getSeed());
|
world.setRawWorldSeed(event.getWorld().getSeed());
|
||||||
Engine engine = getEngine(event.getWorld());
|
Engine engine = getEngine(event.getWorld());
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
@@ -154,6 +145,20 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) {
|
||||||
|
Location location = new Location(world, 0, 64, 0);
|
||||||
|
PaperLib.getChunkAtAsync(location)
|
||||||
|
.thenAccept(c -> {
|
||||||
|
World w = c.getWorld();
|
||||||
|
if (!w.getSpawnLocation().equals(location))
|
||||||
|
return;
|
||||||
|
w.setSpawnLocation(location.add(0, w.getHighestBlockYAt(location) - 64, 0));
|
||||||
|
});
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
private void setupEngine() {
|
private void setupEngine() {
|
||||||
IrisData data = IrisData.get(dataLocation);
|
IrisData data = IrisData.get(dataLocation);
|
||||||
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
|
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
|
||||||
@@ -297,7 +302,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
hotloader.interrupt();
|
hotloader.interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
getEngine().close();
|
final Engine engine = getEngine();
|
||||||
|
if (engine != null && !engine.isClosed())
|
||||||
|
engine.close();
|
||||||
folder.clear();
|
folder.clear();
|
||||||
populators.clear();
|
populators.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ import com.volmit.iris.util.data.DoubleArrayUtils;
|
|||||||
*/
|
*/
|
||||||
public class AtomicAverage {
|
public class AtomicAverage {
|
||||||
protected final AtomicDoubleArray values;
|
protected final AtomicDoubleArray values;
|
||||||
protected int cursor;
|
protected transient int cursor;
|
||||||
private double average;
|
private transient double average;
|
||||||
private double lastSum;
|
private transient double lastSum;
|
||||||
private boolean dirty;
|
private transient boolean dirty;
|
||||||
private boolean brandNew;
|
private transient boolean brandNew;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an average holder
|
* Create an average holder
|
||||||
@@ -57,7 +57,7 @@ public class AtomicAverage {
|
|||||||
*
|
*
|
||||||
* @param i the value
|
* @param i the value
|
||||||
*/
|
*/
|
||||||
public void put(double i) {
|
public synchronized void put(double i) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import com.volmit.iris.util.math.RNG;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
public class KList<T> extends ArrayList<T> implements List<T> {
|
public class KList<T> extends ArrayList<T> implements List<T> {
|
||||||
@@ -65,6 +67,10 @@ public class KList<T> extends ArrayList<T> implements List<T> {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Collector<T, ?, KList<T>> collector() {
|
||||||
|
return Collectors.toCollection(KList::new);
|
||||||
|
}
|
||||||
|
|
||||||
public static KList<String> asStringList(List<?> oo) {
|
public static KList<String> asStringList(List<?> oo) {
|
||||||
KList<String> s = new KList<String>();
|
KList<String> s = new KList<String>();
|
||||||
|
|
||||||
|
|||||||
@@ -18,29 +18,67 @@
|
|||||||
|
|
||||||
package com.volmit.iris.util.collection;
|
package com.volmit.iris.util.collection;
|
||||||
|
|
||||||
import java.util.Collection;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
public class KSet<T> extends HashSet<T> {
|
import java.io.Serializable;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class KSet<T> extends AbstractSet<T> implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final ConcurrentHashMap<T, Boolean> map;
|
||||||
|
|
||||||
public KSet() {
|
public KSet() {
|
||||||
super();
|
map = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KSet(Collection<? extends T> c) {
|
public KSet(Collection<? extends T> c) {
|
||||||
super(c);
|
this();
|
||||||
|
addAll(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KSet(int initialCapacity, float loadFactor) {
|
public KSet(int initialCapacity, float loadFactor) {
|
||||||
super(initialCapacity, loadFactor);
|
map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KSet(int initialCapacity) {
|
public KSet(int initialCapacity) {
|
||||||
super(initialCapacity);
|
map = new ConcurrentHashMap<>(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return map.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(T t) {
|
||||||
|
return map.putIfAbsent(t, Boolean.TRUE) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return map.remove(o) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return map.keySet().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KSet<T> copy() {
|
public KSet<T> copy() {
|
||||||
return new KSet<T>(this);
|
return new KSet<>(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
package com.volmit.iris.util.io;
|
package com.volmit.iris.util.io;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
|
|
||||||
@@ -134,8 +138,7 @@ public class IO {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var fin = new FileInputStream(file)) {
|
try (var din = new CheckedInputStream(readDeterministic(file), crc)) {
|
||||||
var din = new CheckedInputStream(fin, crc);
|
|
||||||
fullTransfer(din, new VoidOutputStream(), 8192);
|
fullTransfer(din, new VoidOutputStream(), 8192);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
@@ -152,10 +155,43 @@ public class IO {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static InputStream readDeterministic(File file) throws IOException {
|
||||||
|
if (!file.getName().endsWith(".json"))
|
||||||
|
return new FileInputStream(file);
|
||||||
|
|
||||||
|
JsonElement json;
|
||||||
|
try (FileReader reader = new FileReader(file)) {
|
||||||
|
json = JsonParser.parseReader(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
var queue = new LinkedList<JsonElement>();
|
||||||
|
queue.add(json);
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
var element = queue.pop();
|
||||||
|
Collection<JsonElement> add = List.of();
|
||||||
|
|
||||||
|
if (element instanceof JsonObject obj) {
|
||||||
|
var map = obj.asMap();
|
||||||
|
var sorted = new TreeMap<>(map);
|
||||||
|
map.clear();
|
||||||
|
map.putAll(sorted);
|
||||||
|
|
||||||
|
add = sorted.values();
|
||||||
|
} else if (element instanceof JsonArray array) {
|
||||||
|
add = array.asList();
|
||||||
|
}
|
||||||
|
|
||||||
|
add.stream().filter(e -> e.isJsonObject() || e.isJsonArray()).forEach(queue::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toInputStream(json.toString());
|
||||||
|
}
|
||||||
|
|
||||||
public static String hash(File b) {
|
public static String hash(File b) {
|
||||||
try {
|
try {
|
||||||
MessageDigest d = MessageDigest.getInstance("SHA-256");
|
MessageDigest d = MessageDigest.getInstance("SHA-256");
|
||||||
DigestInputStream din = new DigestInputStream(new FileInputStream(b), d);
|
DigestInputStream din = new DigestInputStream(readDeterministic(b), d);
|
||||||
fullTransfer(din, new VoidOutputStream(), 8192);
|
fullTransfer(din, new VoidOutputStream(), 8192);
|
||||||
din.close();
|
din.close();
|
||||||
return bytesToHex(din.getMessageDigest().digest());
|
return bytesToHex(din.getMessageDigest().digest());
|
||||||
|
|||||||
@@ -31,16 +31,22 @@ public class JarScanner {
|
|||||||
private final KSet<Class<?>> classes;
|
private final KSet<Class<?>> classes;
|
||||||
private final File jar;
|
private final File jar;
|
||||||
private final String superPackage;
|
private final String superPackage;
|
||||||
|
private final boolean report;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a scanner
|
* Create a scanner
|
||||||
*
|
*
|
||||||
* @param jar the path to the jar
|
* @param jar the path to the jar
|
||||||
*/
|
*/
|
||||||
public JarScanner(File jar, String superPackage) {
|
public JarScanner(File jar, String superPackage, boolean report) {
|
||||||
this.jar = jar;
|
this.jar = jar;
|
||||||
this.classes = new KSet<>();
|
this.classes = new KSet<>();
|
||||||
this.superPackage = superPackage;
|
this.superPackage = superPackage;
|
||||||
|
this.report = report;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JarScanner(File jar, String superPackage) {
|
||||||
|
this(jar, superPackage, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +71,8 @@ public class JarScanner {
|
|||||||
try {
|
try {
|
||||||
Class<?> clazz = Class.forName(c);
|
Class<?> clazz = Class.forName(c);
|
||||||
classes.add(clazz);
|
classes.add(clazz);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (Throwable e) {
|
||||||
|
if (!report) continue;
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ package com.volmit.iris.util.mantle;
|
|||||||
import com.google.common.util.concurrent.AtomicDouble;
|
import com.google.common.util.concurrent.AtomicDouble;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.IrisSettings;
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.data.cache.Cache;
|
import com.volmit.iris.engine.data.cache.Cache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.mantle.EngineMantle;
|
import com.volmit.iris.engine.mantle.EngineMantle;
|
||||||
import com.volmit.iris.engine.mantle.MantleWriter;
|
import com.volmit.iris.engine.mantle.MantleWriter;
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.documentation.BlockCoordinates;
|
import com.volmit.iris.util.documentation.BlockCoordinates;
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||||
@@ -51,8 +50,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
|
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
|
||||||
@@ -60,18 +57,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public class Mantle {
|
public class Mantle {
|
||||||
private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true");
|
|
||||||
private final File dataFolder;
|
private final File dataFolder;
|
||||||
@Getter
|
@Getter
|
||||||
private final int worldHeight;
|
private final int worldHeight;
|
||||||
private final Map<Long, Long> lastUse;
|
private final Map<Long, Long> lastUse;
|
||||||
@Getter
|
|
||||||
private final Map<Long, TectonicPlate> loadedRegions;
|
private final Map<Long, TectonicPlate> loadedRegions;
|
||||||
private final HyperLock hyperLock;
|
private final HyperLock hyperLock;
|
||||||
private final AtomicBoolean closed;
|
private final AtomicBoolean closed;
|
||||||
private final MultiBurst ioBurst;
|
private final MultiBurst ioBurst;
|
||||||
private final AtomicBoolean ioTrim;
|
private final AtomicBoolean ioTrim;
|
||||||
private final AtomicBoolean ioTectonicUnload;
|
private final AtomicBoolean ioTectonicUnload;
|
||||||
|
private final AtomicDouble adjustedIdleDuration;
|
||||||
|
private final KSet<Long> toUnload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new mantle
|
* Create a new mantle
|
||||||
@@ -91,6 +88,8 @@ public class Mantle {
|
|||||||
loadedRegions = new KMap<>();
|
loadedRegions = new KMap<>();
|
||||||
lastUse = new KMap<>();
|
lastUse = new KMap<>();
|
||||||
ioBurst = MultiBurst.burst;
|
ioBurst = MultiBurst.burst;
|
||||||
|
adjustedIdleDuration = new AtomicDouble(0);
|
||||||
|
toUnload = new KSet<>();
|
||||||
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +102,7 @@ public class Mantle {
|
|||||||
* @return the file
|
* @return the file
|
||||||
*/
|
*/
|
||||||
public static File fileForRegion(File folder, int x, int z) {
|
public static File fileForRegion(File folder, int x, int z) {
|
||||||
return fileForRegion(folder, key(x, z));
|
return fileForRegion(folder, key(x, z), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,12 +112,28 @@ public class Mantle {
|
|||||||
* @param key the region key
|
* @param key the region key
|
||||||
* @return the file
|
* @return the file
|
||||||
*/
|
*/
|
||||||
public static File fileForRegion(File folder, Long key) {
|
public static File fileForRegion(File folder, Long key, boolean convert) {
|
||||||
File f = new File(folder, "p." + key + ".ttp.lz4b");
|
File f = oldFileForRegion(folder, key);
|
||||||
if (!f.getParentFile().exists()) {
|
File fv = new File(folder, "pv." + key + ".ttp.lz4b");
|
||||||
f.getParentFile().mkdirs();
|
if (f.exists() && !fv.exists() && convert)
|
||||||
|
return f;
|
||||||
|
|
||||||
|
if (!fv.getParentFile().exists()) {
|
||||||
|
fv.getParentFile().mkdirs();
|
||||||
}
|
}
|
||||||
return f;
|
return fv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the old file for the given region
|
||||||
|
*
|
||||||
|
* @param folder the data folder
|
||||||
|
* @param key the region key
|
||||||
|
* @return the file
|
||||||
|
*/
|
||||||
|
public static File oldFileForRegion(File folder, Long key) {
|
||||||
|
return new File(folder, "p." + key + ".ttp.lz4b");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -210,7 +225,7 @@ public class Mantle {
|
|||||||
@RegionCoordinates
|
@RegionCoordinates
|
||||||
public boolean hasTectonicPlate(int x, int z) {
|
public boolean hasTectonicPlate(int x, int z) {
|
||||||
Long k = key(x, z);
|
Long k = key(x, z);
|
||||||
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k).exists();
|
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k, true).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -359,16 +374,19 @@ public class Mantle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
closed.set(true);
|
closed.set(true);
|
||||||
BurstExecutor b = ioBurst.burst(loadedRegions.size());
|
hyperLock.disable();
|
||||||
for (Long i : loadedRegions.keySet()) {
|
BurstExecutor b = ioBurst.burst(toUnload.size());
|
||||||
b.queue(() -> {
|
loadedRegions.forEach((i, plate) -> b.queue(() -> {
|
||||||
try {
|
try {
|
||||||
loadedRegions.get(i).write(fileForRegion(dataFolder, i));
|
plate.close();
|
||||||
} catch (IOException e) {
|
plate.write(fileForRegion(dataFolder, i, false));
|
||||||
e.printStackTrace();
|
oldFileForRegion(dataFolder, i).delete();
|
||||||
}
|
} catch (Throwable e) {
|
||||||
});
|
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
|
||||||
}
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
loadedRegions.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
b.complete();
|
b.complete();
|
||||||
@@ -376,7 +394,6 @@ public class Mantle {
|
|||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadedRegions.clear();
|
|
||||||
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,16 +409,6 @@ public class Mantle {
|
|||||||
return numberOfEntries * bytesPerEntry;
|
return numberOfEntries * bytesPerEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final AtomicDouble adjustedIdleDuration = new AtomicDouble(0);
|
|
||||||
@Getter
|
|
||||||
private final AtomicInteger forceAggressiveThreshold = new AtomicInteger(30);
|
|
||||||
@Getter
|
|
||||||
private final AtomicLong oldestTectonicPlate = new AtomicLong(0);
|
|
||||||
private final ReentrantLock unloadLock = new ReentrantLock();
|
|
||||||
@Getter
|
|
||||||
private final KList<Long> toUnload = new KList<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save & unload regions that have not been used for more than the
|
* Save & unload regions that have not been used for more than the
|
||||||
* specified amount of milliseconds
|
* specified amount of milliseconds
|
||||||
@@ -414,93 +421,81 @@ public class Mantle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
adjustedIdleDuration.set(baseIdleDuration);
|
adjustedIdleDuration.set(baseIdleDuration);
|
||||||
|
if (loadedRegions.size() > tectonicLimit) {
|
||||||
if (loadedRegions != null) {
|
// todo update this correctly and maybe do something when its above a 100%
|
||||||
if (loadedRegions.size() > tectonicLimit) {
|
adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
|
||||||
// todo update this correctly and maybe do something when its above a 100%
|
|
||||||
adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ioTrim.set(true);
|
ioTrim.set(true);
|
||||||
unloadLock.lock();
|
|
||||||
try {
|
try {
|
||||||
if (lastUse != null && IrisEngineSVC.instance != null) {
|
double adjustedIdleDuration = this.adjustedIdleDuration.get();
|
||||||
if (!lastUse.isEmpty()) {
|
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration, 0));
|
||||||
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0));
|
|
||||||
for (long i : new ArrayList<>(lastUse.keySet())) {
|
|
||||||
double finalAdjustedIdleDuration = adjustedIdleDuration.get();
|
|
||||||
hyperLock.withLong(i, () -> {
|
|
||||||
Long lastUseTime = lastUse.get(i);
|
|
||||||
if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) {
|
|
||||||
toUnload.add(i);
|
|
||||||
Iris.debug("Tectonic Region added to unload");
|
|
||||||
IrisEngineSVC.instance.trimActiveAlive.reset();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (lastUse.isEmpty()) return;
|
||||||
|
double unloadTime = M.ms() - adjustedIdleDuration;
|
||||||
|
for (long id : lastUse.keySet()) {
|
||||||
|
hyperLock.withLong(id, () -> {
|
||||||
|
Long lastUseTime = lastUse.get(id);
|
||||||
|
if (lastUseTime != null && lastUseTime < unloadTime) {
|
||||||
|
toUnload.add(id);
|
||||||
|
Iris.debug("Tectonic Region added to unload");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
} finally {
|
} finally {
|
||||||
ioTrim.set(false);
|
ioTrim.set(false);
|
||||||
unloadLock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int unloadTectonicPlate(int tectonicLimit) {
|
public synchronized int unloadTectonicPlate(int tectonicLimit) {
|
||||||
|
if (closed.get()) {
|
||||||
|
throw new RuntimeException("The Mantle is closed");
|
||||||
|
}
|
||||||
|
|
||||||
AtomicInteger i = new AtomicInteger();
|
AtomicInteger i = new AtomicInteger();
|
||||||
unloadLock.lock();
|
BurstExecutor burst = ioBurst.burst(toUnload.size());
|
||||||
BurstExecutor burst = null;
|
burst.setMulticore(toUnload.size() > tectonicLimit);
|
||||||
if (IrisEngineSVC.instance != null) {
|
|
||||||
try {
|
ioTectonicUnload.set(true);
|
||||||
KList<Long> copy = toUnload.copy();
|
try {
|
||||||
if (!disableClear) toUnload.clear();
|
for (long id : toUnload) {
|
||||||
burst = MultiBurst.burst.burst(copy.size());
|
burst.queue(() -> hyperLock.withLong(id, () -> {
|
||||||
burst.setMulticore(copy.size() > tectonicLimit);
|
TectonicPlate m = loadedRegions.get(id);
|
||||||
for (int j = 0; j < copy.size(); j++) {
|
if (m == null) {
|
||||||
Long id = copy.get(j);
|
Iris.debug("Tectonic Plate was added to unload while not loaded " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||||
if (id == null) {
|
toUnload.remove(id);
|
||||||
Iris.error("Null id in unloadTectonicPlate at index " + j);
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
burst.queue(() ->
|
if (m.inUse()) {
|
||||||
hyperLock.withLong(id, () -> {
|
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
||||||
TectonicPlate m = loadedRegions.get(id);
|
lastUse.put(id, M.ms());
|
||||||
if (m != null) {
|
toUnload.remove(id);
|
||||||
if (m.inUse()) {
|
return;
|
||||||
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
}
|
||||||
if (disableClear) toUnload.remove(id);
|
|
||||||
lastUse.put(id, M.ms());
|
try {
|
||||||
return;
|
m.write(fileForRegion(dataFolder, id, false));
|
||||||
}
|
oldFileForRegion(dataFolder, id).delete();
|
||||||
try {
|
loadedRegions.remove(id);
|
||||||
m.write(fileForRegion(dataFolder, id));
|
lastUse.remove(id);
|
||||||
loadedRegions.remove(id);
|
toUnload.remove(id);
|
||||||
lastUse.remove(id);
|
i.incrementAndGet();
|
||||||
if (disableClear) toUnload.remove(id);
|
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||||
i.incrementAndGet();
|
} catch (IOException e) {
|
||||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
Iris.reportError(e);
|
||||||
IrisEngineSVC.instance.unloadActiveAlive.reset();
|
}
|
||||||
} catch (IOException e) {
|
}));
|
||||||
Iris.reportError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
burst.complete();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
if (burst != null)
|
|
||||||
burst.complete();
|
|
||||||
} finally {
|
|
||||||
unloadLock.unlock();
|
|
||||||
ioTectonicUnload.set(true);
|
|
||||||
}
|
}
|
||||||
return i.get();
|
burst.complete();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
burst.complete();
|
||||||
|
} finally {
|
||||||
|
ioTectonicUnload.set(false);
|
||||||
}
|
}
|
||||||
return i.get();
|
return i.get();
|
||||||
}
|
}
|
||||||
@@ -516,7 +511,7 @@ public class Mantle {
|
|||||||
*/
|
*/
|
||||||
@RegionCoordinates
|
@RegionCoordinates
|
||||||
private TectonicPlate get(int x, int z) {
|
private TectonicPlate get(int x, int z) {
|
||||||
if (ioTrim.get()) {
|
if (ioTrim.get() || ioTectonicUnload.get()) {
|
||||||
try {
|
try {
|
||||||
return getSafe(x, z).get();
|
return getSafe(x, z).get();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -576,7 +571,7 @@ public class Mantle {
|
|||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
try {
|
try {
|
||||||
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
|
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
|
||||||
region = TectonicPlate.read(worldHeight, file);
|
region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv."));
|
||||||
|
|
||||||
if (region.getX() != x || region.getZ() != z) {
|
if (region.getX() != x || region.getZ() != z) {
|
||||||
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
|
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
|
||||||
@@ -626,6 +621,14 @@ public class Mantle {
|
|||||||
return loadedRegions.size();
|
return loadedRegions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getUnloadRegionCount() {
|
||||||
|
return toUnload.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAdjustedIdleDuration() {
|
||||||
|
return adjustedIdleDuration.get();
|
||||||
|
}
|
||||||
|
|
||||||
public <T> void set(int x, int y, int z, MatterSlice<T> slice) {
|
public <T> void set(int x, int y, int z, MatterSlice<T> slice) {
|
||||||
if (slice.isEmpty()) {
|
if (slice.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package com.volmit.iris.util.mantle;
|
package com.volmit.iris.util.mantle;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.util.data.Varint;
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.function.Consumer4;
|
import com.volmit.iris.util.function.Consumer4;
|
||||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||||
@@ -30,7 +31,8 @@ import lombok.Getter;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||||
|
|
||||||
@@ -45,7 +47,8 @@ public class MantleChunk {
|
|||||||
private final int z;
|
private final int z;
|
||||||
private final AtomicIntegerArray flags;
|
private final AtomicIntegerArray flags;
|
||||||
private final AtomicReferenceArray<Matter> sections;
|
private final AtomicReferenceArray<Matter> sections;
|
||||||
private final AtomicInteger ref = new AtomicInteger();
|
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
|
||||||
|
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a mantle chunk
|
* Create a mantle chunk
|
||||||
@@ -72,11 +75,12 @@ public class MantleChunk {
|
|||||||
* @throws IOException shit happens
|
* @throws IOException shit happens
|
||||||
* @throws ClassNotFoundException shit happens
|
* @throws ClassNotFoundException shit happens
|
||||||
*/
|
*/
|
||||||
public MantleChunk(int sectionHeight, CountingDataInputStream din) throws IOException {
|
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
|
||||||
this(sectionHeight, din.readByte(), din.readByte());
|
this(sectionHeight, din.readByte(), din.readByte());
|
||||||
int s = din.readByte();
|
int s = din.readByte();
|
||||||
|
int l = version < 0 ? flags.length() : Varint.readUnsignedVarInt(din);
|
||||||
|
|
||||||
for (int i = 0; i < flags.length(); i++) {
|
for (int i = 0; i < flags.length() && i < l; i++) {
|
||||||
flags.set(i, din.readBoolean() ? 1 : 0);
|
flags.set(i, din.readBoolean() ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +89,10 @@ public class MantleChunk {
|
|||||||
long size = din.readInt();
|
long size = din.readInt();
|
||||||
if (size == 0) continue;
|
if (size == 0) continue;
|
||||||
long start = din.count();
|
long start = din.count();
|
||||||
|
if (i >= sectionHeight) {
|
||||||
|
din.skipTo(start + size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sections.set(i, Matter.readDin(din));
|
sections.set(i, Matter.readDin(din));
|
||||||
@@ -103,20 +111,27 @@ public class MantleChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() throws InterruptedException {
|
||||||
|
closed.set(true);
|
||||||
|
ref.acquire(Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean inUse() {
|
public boolean inUse() {
|
||||||
return ref.get() > 0;
|
return ref.availablePermits() < Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MantleChunk use() {
|
public MantleChunk use() {
|
||||||
ref.incrementAndGet();
|
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
|
||||||
|
ref.acquireUninterruptibly();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
ref.decrementAndGet();
|
ref.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flag(MantleFlag flag, boolean f) {
|
public void flag(MantleFlag flag, boolean f) {
|
||||||
|
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
|
||||||
flags.set(flag.ordinal(), f ? 1 : 0);
|
flags.set(flag.ordinal(), f ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +216,7 @@ public class MantleChunk {
|
|||||||
dos.writeByte(x);
|
dos.writeByte(x);
|
||||||
dos.writeByte(z);
|
dos.writeByte(z);
|
||||||
dos.writeByte(sections.length());
|
dos.writeByte(sections.length());
|
||||||
|
Varint.writeUnsignedVarInt(flags.length(), dos);
|
||||||
|
|
||||||
for (int i = 0; i < flags.length(); i++) {
|
for (int i = 0; i < flags.length(); i++) {
|
||||||
dos.writeBoolean(flags.get(i) == 1);
|
dos.writeBoolean(flags.get(i) == 1);
|
||||||
|
|||||||
@@ -19,9 +19,10 @@
|
|||||||
package com.volmit.iris.util.mantle;
|
package com.volmit.iris.util.mantle;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.engine.EnginePanic;
|
import com.volmit.iris.engine.EnginePanic;
|
||||||
import com.volmit.iris.engine.data.cache.Cache;
|
import com.volmit.iris.engine.data.cache.Cache;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.data.Varint;
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
@@ -44,7 +45,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
|
|||||||
* Tectonic Plates are fully atomic & thread safe
|
* Tectonic Plates are fully atomic & thread safe
|
||||||
*/
|
*/
|
||||||
public class TectonicPlate {
|
public class TectonicPlate {
|
||||||
private static final KSet<Thread> errors = new KSet<>();
|
private static final ThreadLocal<Boolean> errors = ThreadLocal.withInitial(() -> false);
|
||||||
|
public static final int MISSING = -1;
|
||||||
|
public static final int CURRENT = 0;
|
||||||
|
|
||||||
private final int sectionHeight;
|
private final int sectionHeight;
|
||||||
private final AtomicReferenceArray<MantleChunk> chunks;
|
private final AtomicReferenceArray<MantleChunk> chunks;
|
||||||
@@ -74,11 +77,12 @@ public class TectonicPlate {
|
|||||||
* @param din the data input
|
* @param din the data input
|
||||||
* @throws IOException shit happens yo
|
* @throws IOException shit happens yo
|
||||||
*/
|
*/
|
||||||
public TectonicPlate(int worldHeight, CountingDataInputStream din) throws IOException {
|
public TectonicPlate(int worldHeight, CountingDataInputStream din, boolean versioned) throws IOException {
|
||||||
this(worldHeight, din.readInt(), din.readInt());
|
this(worldHeight, din.readInt(), din.readInt());
|
||||||
if (!din.markSupported())
|
if (!din.markSupported())
|
||||||
throw new IOException("Mark not supported!");
|
throw new IOException("Mark not supported!");
|
||||||
|
|
||||||
|
int v = versioned ? Varint.readUnsignedVarInt(din) : MISSING;
|
||||||
for (int i = 0; i < chunks.length(); i++) {
|
for (int i = 0; i < chunks.length(); i++) {
|
||||||
long size = din.readInt();
|
long size = din.readInt();
|
||||||
if (size == 0) continue;
|
if (size == 0) continue;
|
||||||
@@ -86,7 +90,7 @@ public class TectonicPlate {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Iris.addPanic("read-chunk", "Chunk[" + i + "]");
|
Iris.addPanic("read-chunk", "Chunk[" + i + "]");
|
||||||
chunks.set(i, new MantleChunk(sectionHeight, din));
|
chunks.set(i, new MantleChunk(v, sectionHeight, din));
|
||||||
EnginePanic.saveLast();
|
EnginePanic.saveLast();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
long end = start + size;
|
long end = start + size;
|
||||||
@@ -103,7 +107,7 @@ public class TectonicPlate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TectonicPlate read(int worldHeight, File file) throws IOException {
|
public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException {
|
||||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
||||||
fc.lock();
|
fc.lock();
|
||||||
|
|
||||||
@@ -111,10 +115,10 @@ public class TectonicPlate {
|
|||||||
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
|
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
|
||||||
BufferedInputStream bis = new BufferedInputStream(lz4);
|
BufferedInputStream bis = new BufferedInputStream(lz4);
|
||||||
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
|
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
|
||||||
return new TectonicPlate(worldHeight, din);
|
return new TectonicPlate(worldHeight, din, versioned);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (errors.remove(Thread.currentThread())) {
|
if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
|
||||||
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
|
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
|
||||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
||||||
fc.lock();
|
fc.lock();
|
||||||
@@ -124,6 +128,7 @@ public class TectonicPlate {
|
|||||||
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
errors.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +141,15 @@ public class TectonicPlate {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() throws InterruptedException {
|
||||||
|
for (int i = 0; i < chunks.length(); i++) {
|
||||||
|
MantleChunk chunk = chunks.get(i);
|
||||||
|
if (chunk != null) {
|
||||||
|
chunk.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a chunk exists in this plate or not (same as get(x, z) != null)
|
* Check if a chunk exists in this plate or not (same as get(x, z) != null)
|
||||||
*
|
*
|
||||||
@@ -208,15 +222,13 @@ public class TectonicPlate {
|
|||||||
*/
|
*/
|
||||||
public void write(File file) throws IOException {
|
public void write(File file) throws IOException {
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) {
|
File temp = File.createTempFile("iris-tectonic-plate", ".bin");
|
||||||
fc.lock();
|
try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(temp)))) {
|
||||||
|
write(dos);
|
||||||
OutputStream fos = Channels.newOutputStream(fc);
|
|
||||||
try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(fos))) {
|
|
||||||
write(dos);
|
|
||||||
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||||
|
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||||
|
temp.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -228,6 +240,7 @@ public class TectonicPlate {
|
|||||||
public void write(DataOutputStream dos) throws IOException {
|
public void write(DataOutputStream dos) throws IOException {
|
||||||
dos.writeInt(x);
|
dos.writeInt(x);
|
||||||
dos.writeInt(z);
|
dos.writeInt(z);
|
||||||
|
Varint.writeUnsignedVarInt(CURRENT, dos);
|
||||||
|
|
||||||
var bytes = new ByteArrayOutputStream(8192);
|
var bytes = new ByteArrayOutputStream(8192);
|
||||||
var sub = new DataOutputStream(bytes);
|
var sub = new DataOutputStream(bytes);
|
||||||
@@ -249,6 +262,6 @@ public class TectonicPlate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addError() {
|
public static void addError() {
|
||||||
errors.add(Thread.currentThread());
|
errors.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class Spiraler {
|
|||||||
|
|
||||||
public void next() {
|
public void next() {
|
||||||
if ((-sizeX / 2 <= x) && (x <= sizeX / 2) && (-sizeZ / 2 <= z) && (z <= sizeZ / 2)) {
|
if ((-sizeX / 2 <= x) && (x <= sizeX / 2) && (-sizeZ / 2 <= z) && (z <= sizeZ / 2)) {
|
||||||
spiraled.on(x + ox, z + ox);
|
spiraled.on(x + ox, z + oz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) {
|
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) {
|
||||||
|
|||||||
@@ -154,15 +154,16 @@ public interface Matter {
|
|||||||
matter.putSlice(type, slice);
|
matter.putSlice(type, slice);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
long end = start + size;
|
long end = start + size;
|
||||||
Iris.error("Failed to read matter slice, skipping it.");
|
if (!(e instanceof ClassNotFoundException)) {
|
||||||
Iris.addPanic("read.byte.range", start + " " + end);
|
Iris.error("Failed to read matter slice, skipping it.");
|
||||||
Iris.addPanic("read.byte.current", din.count() + "");
|
Iris.addPanic("read.byte.range", start + " " + end);
|
||||||
Iris.reportError(e);
|
Iris.addPanic("read.byte.current", din.count() + "");
|
||||||
e.printStackTrace();
|
Iris.reportError(e);
|
||||||
Iris.panic();
|
e.printStackTrace();
|
||||||
|
Iris.panic();
|
||||||
|
TectonicPlate.addError();
|
||||||
|
}
|
||||||
din.skipTo(end);
|
din.skipTo(end);
|
||||||
TectonicPlate.addError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
package com.volmit.iris.util.misc;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import net.md_5.bungee.api.chat.ClickEvent;
|
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import oshi.SystemInfo;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Hastebin {
|
|
||||||
|
|
||||||
public static void enviornment(CommandSender sender) {
|
|
||||||
// Construct the server information
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
SystemInfo systemInfo = new SystemInfo();
|
|
||||||
KList<String> disks = new KList<>(getHardware.getDisk());
|
|
||||||
KList<String> interfaces = new KList<>(getHardware.getInterfaces());
|
|
||||||
KList<String> displays = new KList<>(getHardware.getEDID());
|
|
||||||
KList<String> sensors = new KList<>(getHardware.getSensors());
|
|
||||||
KList<String> gpus = new KList<>(getHardware.getGraphicsCards());
|
|
||||||
KList<String> powersources = new KList<>(getHardware.getPowerSources());
|
|
||||||
|
|
||||||
KList<World> IrisWorlds = new KList<>();
|
|
||||||
KList<World> BukkitWorlds = new KList<>();
|
|
||||||
|
|
||||||
for (World w : Bukkit.getServer().getWorlds()) {
|
|
||||||
try {
|
|
||||||
Engine engine = IrisToolbelt.access(w).getEngine();
|
|
||||||
if (engine != null) {
|
|
||||||
IrisWorlds.add(w);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
BukkitWorlds.add(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(" -- == Iris Info == -- \n");
|
|
||||||
sb.append("Iris Version Version: ").append(Iris.instance.getDescription().getVersion()).append("\n");
|
|
||||||
sb.append("- Iris Worlds");
|
|
||||||
for (World w : IrisWorlds.copy()) {
|
|
||||||
sb.append(" - ").append(w.getName());
|
|
||||||
}
|
|
||||||
sb.append("- Bukkit Worlds");
|
|
||||||
for (World w : BukkitWorlds.copy()) {
|
|
||||||
sb.append(" - ").append(w.getName());
|
|
||||||
}
|
|
||||||
sb.append(" -- == Platform Overview == -- " + "\n");
|
|
||||||
sb.append("Server Type: ").append(Bukkit.getVersion()).append("\n");
|
|
||||||
sb.append("Server Uptime: ").append(Form.stampTime(systemInfo.getOperatingSystem().getSystemUptime())).append("\n");
|
|
||||||
sb.append("Version: ").append(Platform.getVersion()).append(" - Platform: ").append(Platform.getName()).append("\n");
|
|
||||||
sb.append("Java Vendor: ").append(Platform.ENVIRONMENT.getJavaVendor()).append(" - Java Version: ").append(Platform.ENVIRONMENT.getJavaVersion()).append("\n");
|
|
||||||
sb.append(" -- == Processor Overview == -- " + "\n");
|
|
||||||
sb.append("CPU Model: ").append(getHardware.getCPUModel());
|
|
||||||
sb.append("CPU Architecture: ").append(Platform.CPU.getArchitecture()).append(" Available Processors: ").append(Platform.CPU.getAvailableProcessors()).append("\n");
|
|
||||||
sb.append("CPU Load: ").append(Form.pc(Platform.CPU.getCPULoad())).append(" CPU Live Process Load: ").append(Form.pc(Platform.CPU.getLiveProcessCPULoad())).append("\n");
|
|
||||||
sb.append("-=" + " Graphics " + "=- " + "\n");
|
|
||||||
for (String gpu : gpus) {
|
|
||||||
sb.append(" ").append(gpu).append("\n");
|
|
||||||
}
|
|
||||||
sb.append(" -- == Memory Information == -- " + "\n");
|
|
||||||
sb.append("Physical Memory - Total: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getUsedMemory())).append("\n");
|
|
||||||
sb.append("Virtual Memory - Total: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getUsedMemory())).append("\n");
|
|
||||||
sb.append(" -- == Storage Information == -- " + "\n");
|
|
||||||
for (String disk : disks) {
|
|
||||||
sb.append(" ").append(sb.append(disk)).append("\n");
|
|
||||||
}
|
|
||||||
sb.append(" -- == Interface Information == -- "+ "\n" );
|
|
||||||
for (String inter : interfaces) {
|
|
||||||
sb.append(" ").append(inter).append("\n");
|
|
||||||
}
|
|
||||||
sb.append(" -- == Display Information == -- "+ "\n" );
|
|
||||||
for (String display : displays) {
|
|
||||||
sb.append(display).append("\n");
|
|
||||||
}
|
|
||||||
sb.append(" -- == Sensor Information == -- " + "\n");
|
|
||||||
for (String sensor : sensors) {
|
|
||||||
sb.append(" ").append(sensor).append("\n");
|
|
||||||
}
|
|
||||||
sb.append(" -- == Power Information == -- " + "\n");
|
|
||||||
for (String power : powersources) {
|
|
||||||
sb.append(" ").append(power).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String hastebinUrl = uploadToHastebin(sb.toString());
|
|
||||||
|
|
||||||
// Create the clickable message
|
|
||||||
TextComponent message = new TextComponent("[Link]");
|
|
||||||
TextComponent link = new TextComponent(hastebinUrl);
|
|
||||||
link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, hastebinUrl));
|
|
||||||
message.addExtra(link);
|
|
||||||
|
|
||||||
// Send the clickable message to the player
|
|
||||||
sender.spigot().sendMessage(message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
sender.sendMessage(C.DARK_RED + "Failed to upload server information to Hastebin.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String uploadToHastebin(String content) throws Exception {
|
|
||||||
URL url = new URL("https://paste.bytecode.ninja/documents");
|
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setRequestMethod("POST");
|
|
||||||
conn.setRequestProperty("Content-Type", "text/plain");
|
|
||||||
conn.setDoOutput(true);
|
|
||||||
|
|
||||||
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
|
|
||||||
wr.writeBytes(content);
|
|
||||||
wr.flush();
|
|
||||||
wr.close();
|
|
||||||
|
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
|
||||||
String response = br.readLine();
|
|
||||||
br.close();
|
|
||||||
|
|
||||||
return "https://paste.bytecode.ninja/" + response.split("\"")[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
package com.volmit.iris.util.misc;
|
|
||||||
|
|
||||||
import com.sun.management.OperatingSystemMXBean;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
|
|
||||||
@SuppressWarnings("restriction")
|
|
||||||
public class Platform {
|
|
||||||
public static String getVersion() {
|
|
||||||
return getSystem().getVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getName() {
|
|
||||||
return getSystem().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OperatingSystemMXBean getSystem() {
|
|
||||||
return (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ENVIRONMENT {
|
|
||||||
public static boolean canRunBatch() {
|
|
||||||
return getSystem().getName().toLowerCase().contains("windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getJavaHome() {
|
|
||||||
return System.getProperty("java.home");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getJavaVendor() {
|
|
||||||
return System.getProperty("java.vendor");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getJavaVersion() {
|
|
||||||
return System.getProperty("java.version");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class STORAGE {
|
|
||||||
public static long getAbsoluteTotalSpace() {
|
|
||||||
long t = 0;
|
|
||||||
|
|
||||||
for (File i : getRoots()) {
|
|
||||||
t += getTotalSpace(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getTotalSpace() {
|
|
||||||
return getTotalSpace(new File("."));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getTotalSpace(File root) {
|
|
||||||
return root.getTotalSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getAbsoluteFreeSpace() {
|
|
||||||
long t = 0;
|
|
||||||
|
|
||||||
for (File i : getRoots()) {
|
|
||||||
t += getFreeSpace(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getFreeSpace() {
|
|
||||||
return getFreeSpace(new File("."));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getFreeSpace(File root) {
|
|
||||||
return root.getFreeSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getUsedSpace() {
|
|
||||||
return getTotalSpace() - getFreeSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getUsedSpace(File root) {
|
|
||||||
return getTotalSpace(root) - getFreeSpace(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getAbsoluteUsedSpace() {
|
|
||||||
return getAbsoluteTotalSpace() - getAbsoluteFreeSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File[] getRoots() {
|
|
||||||
return File.listRoots();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MEMORY {
|
|
||||||
public static class PHYSICAL {
|
|
||||||
public static long getTotalMemory() {
|
|
||||||
return getSystem().getTotalPhysicalMemorySize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getFreeMemory() {
|
|
||||||
return getSystem().getFreePhysicalMemorySize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getUsedMemory() {
|
|
||||||
return getTotalMemory() - getFreeMemory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class VIRTUAL {
|
|
||||||
public static long getTotalMemory() {
|
|
||||||
return getSystem().getTotalSwapSpaceSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getFreeMemory() {
|
|
||||||
return getSystem().getFreeSwapSpaceSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getUsedMemory() {
|
|
||||||
return getTotalMemory() - getFreeMemory();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getCommittedVirtualMemory() {
|
|
||||||
return getSystem().getCommittedVirtualMemorySize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class CPU {
|
|
||||||
public static int getAvailableProcessors() {
|
|
||||||
return getSystem().getAvailableProcessors();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double getCPULoad() {
|
|
||||||
return getSystem().getSystemCpuLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double getLiveProcessCPULoad() {
|
|
||||||
return getSystem().getProcessCpuLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getArchitecture() {
|
|
||||||
return getSystem().getArch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ public class ServerProperties {
|
|||||||
public static final File SERVER_PROPERTIES;
|
public static final File SERVER_PROPERTIES;
|
||||||
public static final File BUKKIT_YML;
|
public static final File BUKKIT_YML;
|
||||||
|
|
||||||
public static final String LEVEL_NAME = DATA.getProperty("level-name", "world");
|
public static final String LEVEL_NAME;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
String[] args = ProcessHandle.current()
|
String[] args = ProcessHandle.current()
|
||||||
@@ -20,11 +20,13 @@ public class ServerProperties {
|
|||||||
|
|
||||||
String propertiesPath = "server.properties";
|
String propertiesPath = "server.properties";
|
||||||
String bukkitYml = "bukkit.yml";
|
String bukkitYml = "bukkit.yml";
|
||||||
|
String levelName = null;
|
||||||
|
|
||||||
for (int i = 0; i < args.length - 1; i++) {
|
for (int i = 0; i < args.length - 1; i++) {
|
||||||
switch (args[i]) {
|
switch (args[i]) {
|
||||||
case "-c", "--config" -> propertiesPath = args[i + 1];
|
case "-c", "--config" -> propertiesPath = args[i + 1];
|
||||||
case "-b", "--bukkit-settings" -> bukkitYml = args[i + 1];
|
case "-b", "--bukkit-settings" -> bukkitYml = args[i + 1];
|
||||||
|
case "-w", "--level-name", "--world" -> levelName = args[i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,5 +37,8 @@ public class ServerProperties {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (levelName != null) LEVEL_NAME = levelName;
|
||||||
|
else LEVEL_NAME = DATA.getProperty("level-name", "world");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,5 +143,6 @@ public class HyperLock {
|
|||||||
|
|
||||||
public void disable() {
|
public void disable() {
|
||||||
enabled = false;
|
enabled = false;
|
||||||
|
locks.values().forEach(ReentrantLock::lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,15 @@ import com.volmit.iris.core.service.PreservationSVC;
|
|||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.math.M;
|
import com.volmit.iris.util.math.M;
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
public class MultiBurst {
|
public class MultiBurst implements ExecutorService {
|
||||||
|
private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000);
|
||||||
public static final MultiBurst burst = new MultiBurst();
|
public static final MultiBurst burst = new MultiBurst();
|
||||||
private final AtomicLong last;
|
private final AtomicLong last;
|
||||||
private final String name;
|
private final String name;
|
||||||
@@ -144,29 +147,106 @@ public class MultiBurst {
|
|||||||
return getService().submit(o);
|
return getService().submit(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<Runnable> shutdownNow() {
|
||||||
|
close();
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShutdown() {
|
||||||
|
return service == null || service.isShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTerminated() {
|
||||||
|
return service == null || service.isTerminated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
|
||||||
|
return service == null || service.awaitTermination(timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(@NotNull Runnable command) {
|
||||||
|
getService().execute(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> Future<T> submit(@NotNull Callable<T> task) {
|
||||||
|
return getService().submit(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> Future<T> submit(@NotNull Runnable task, T result) {
|
||||||
|
return getService().submit(task, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Future<?> submit(@NotNull Runnable task) {
|
||||||
|
return getService().submit(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException {
|
||||||
|
return getService().invokeAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException {
|
||||||
|
return getService().invokeAll(tasks, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
|
||||||
|
return getService().invokeAny(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
return getService().invokeAny(tasks, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
service.shutdown();
|
close(service);
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
}
|
||||||
try {
|
}
|
||||||
while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
|
|
||||||
Iris.info("Still waiting to shutdown burster...");
|
|
||||||
if (p.getMilliseconds() > 7000) {
|
|
||||||
Iris.warn("Forcing Shutdown...");
|
|
||||||
|
|
||||||
try {
|
public static void close(ExecutorService service) {
|
||||||
service.shutdownNow();
|
service.shutdown();
|
||||||
} catch (Throwable e) {
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
|
try {
|
||||||
|
while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
|
||||||
|
Iris.info("Still waiting to shutdown burster...");
|
||||||
|
if (p.getMilliseconds() > TIMEOUT) {
|
||||||
|
Iris.warn("Forcing Shutdown...");
|
||||||
|
|
||||||
}
|
try {
|
||||||
|
service.shutdownNow();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.reportError(e);
|
|
||||||
}
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
@@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ import net.minecraft.tags.TagKey;
|
|||||||
import net.minecraft.world.entity.EntityDimensions;
|
import net.minecraft.world.entity.EntityDimensions;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
import net.minecraft.world.level.biome.BiomeSource;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
import net.minecraft.world.level.block.Block;
|
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.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
@@ -47,6 +49,9 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
|||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -640,50 +645,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -692,7 +655,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -700,26 +668,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath()))),
|
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getOrThrow(key).generator()
|
source
|
||||||
), Lifecycle.stable());
|
), Lifecycle.stable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,4 +740,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return new ResourceLocation("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
@@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ import net.minecraft.server.WorldLoader;
|
|||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -641,50 +646,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -693,7 +656,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -701,26 +669,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath()))),
|
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getOrThrow(key).generator()
|
source
|
||||||
), Lifecycle.stable());
|
), Lifecycle.stable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,4 +741,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return new ResourceLocation("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ import net.minecraft.server.WorldLoader;
|
|||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -642,50 +647,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -694,7 +657,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -702,26 +670,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath()))),
|
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getOrThrow(key).generator()
|
source
|
||||||
), Lifecycle.stable());
|
), Lifecycle.stable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,4 +742,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return new ResourceLocation("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
@@ -120,6 +121,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,16 @@ import net.minecraft.server.level.ServerLevel;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -667,50 +672,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -719,7 +682,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -727,26 +695,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.getHolder(new ResourceLocation("iris", key.location().getPath())).orElseThrow(),
|
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getOrThrow(key).generator()
|
source
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,4 +767,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return new ResourceLocation("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
@@ -121,6 +122,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,16 @@ import net.minecraft.server.level.ChunkMap;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -671,50 +676,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -723,7 +686,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -731,26 +699,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.getHolder(ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath())).orElseThrow(),
|
dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getOrThrow(key).generator()
|
source
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,4 +771,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
@@ -120,6 +121,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,16 @@ import net.minecraft.server.commands.data.BlockDataAccessor;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -661,50 +666,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -713,7 +676,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -721,26 +689,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.lookupOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.get(ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath())).orElseThrow(),
|
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getValueOrThrow(key).generator()
|
source
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,4 +761,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.tags.StructureTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
import net.minecraft.util.random.WeightedRandomList;
|
||||||
import net.minecraft.world.entity.MobCategory;
|
import net.minecraft.world.entity.MobCategory;
|
||||||
|
import net.minecraft.world.item.EnderEyeItem;
|
||||||
import net.minecraft.world.level.*;
|
import net.minecraft.world.level.*;
|
||||||
import net.minecraft.world.level.biome.*;
|
import net.minecraft.world.level.biome.*;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
@@ -31,6 +33,7 @@ import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
|||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
import net.minecraft.world.level.levelgen.RandomState;
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
@@ -114,6 +117,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
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())
|
if (engine.getDimension().isDisableExplorerMaps())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ import net.minecraft.tags.TagKey;
|
|||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.level.LevelReader;
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
import net.minecraft.world.level.block.Block;
|
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.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
@@ -49,6 +51,9 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|||||||
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
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 org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -660,50 +665,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
|
||||||
var reg = registry();
|
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM);
|
|
||||||
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
|
||||||
var fake = new HashMap<>(old);
|
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
|
||||||
field.set(reg, fake);
|
|
||||||
|
|
||||||
return new Pair<>(
|
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
|
||||||
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
@@ -712,7 +675,12 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, transformer.apply(old));
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
return new AutoClosing(() -> {
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
@@ -720,26 +688,66 @@ public class NMSBinding implements INMSBinding {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.lookupOrThrow(Registries.LEVEL_STEM);
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
if (copy) copy(fake, levelStems);
|
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void register(MappedRegistry<LevelStem> target, Registry<LevelStem> levelStems, Registry<DimensionType> dimensions, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
target.register(key, new LevelStem(
|
target.register(key, new LevelStem(
|
||||||
dimensions.get(ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath())).orElseThrow(),
|
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
levelStems.getValueOrThrow(key).generator()
|
source
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,4 +760,8 @@ public class NMSBinding implements INMSBinding {
|
|||||||
target.register(key, value, info);
|
target.register(key, value, info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_21_R4;
|
||||||
|
|
||||||
|
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.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
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_R4.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.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(ResourceLocation.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()) {
|
||||||
|
ResourceLocation resourceLocation = ResourceLocation.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,311 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_21_R4;
|
||||||
|
|
||||||
|
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.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.collection.KSet;
|
||||||
|
import com.volmit.iris.util.mantle.MantleFlag;
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import com.volmit.iris.util.reflect.WrappedField;
|
||||||
|
import net.minecraft.core.*;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
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.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
|
import net.minecraft.world.level.levelgen.RandomState;
|
||||||
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.generator.CustomChunkGenerator;
|
||||||
|
import org.spigotmc.SpigotWorldConfig;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||||
|
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
||||||
|
private final ChunkGenerator delegate;
|
||||||
|
private final Engine engine;
|
||||||
|
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
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 = ResourceLocation.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 iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey<Level> resourcekey) {
|
||||||
|
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
||||||
|
delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
||||||
|
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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!");
|
||||||
|
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
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,765 @@
|
|||||||
|
package com.volmit.iris.core.nms.v1_21_R4;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
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.util.collection.KList;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
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 lombok.SneakyThrows;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.*;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.WorldLoader;
|
||||||
|
import net.minecraft.server.commands.data.BlockDataAccessor;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.item.component.CustomData;
|
||||||
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
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.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.status.WorldGenContext;
|
||||||
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
|
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 org.bukkit.*;
|
||||||
|
import org.bukkit.block.Biome;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.CraftChunk;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.CraftServer;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.block.CraftBlockState;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.block.CraftBlockStates;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.block.data.CraftBlockData;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
|
||||||
|
import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
|
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.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
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 AtomicCache<WorldLoader.DataLoadContext> dataLoadContext = new AtomicCache<>();
|
||||||
|
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 ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
|
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()), false) != 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()), false);
|
||||||
|
|
||||||
|
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(net.minecraft.nbt.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 net.minecraft.nbt.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 net.minecraft.nbt.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(ResourceLocation.parse(mckey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
||||||
|
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBiomeBaseIdForKey(String key) {
|
||||||
|
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.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 = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
||||||
|
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
||||||
|
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
||||||
|
cf.setAccessible(true);
|
||||||
|
df.setAccessible(true);
|
||||||
|
bf.setAccessible(true);
|
||||||
|
net.minecraft.core.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).getUnsafe());
|
||||||
|
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.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().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 java.awt.Color getBiomeColor(Location location, BiomeColor type) {
|
||||||
|
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
||||||
|
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
||||||
|
var biome = holder.value();
|
||||||
|
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
||||||
|
|
||||||
|
int rgba = switch (type) {
|
||||||
|
case FOG -> biome.getFogColor();
|
||||||
|
case WATER -> biome.getWaterColor();
|
||||||
|
case WATER_FOG -> biome.getWaterFogColor();
|
||||||
|
case SKY -> biome.getSkyColor();
|
||||||
|
case FOLIAGE -> biome.getFoliageColor();
|
||||||
|
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
||||||
|
};
|
||||||
|
if (rgba == 0) {
|
||||||
|
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
||||||
|
return null;
|
||||||
|
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().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.V1213;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSpawnChunkCount(World world) {
|
||||||
|
var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS))
|
||||||
|
.orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS));
|
||||||
|
if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!");
|
||||||
|
return (int) Math.pow(2 * radius + 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(ResourceLocation::toString).forEach(keys::add);
|
||||||
|
registry.getTags()
|
||||||
|
.map(HolderSet.Named::key)
|
||||||
|
.map(TagKey::location)
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.map(s -> "#" + s)
|
||||||
|
.forEach(keys::add);
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
|
var nmsServer = server.getServer();
|
||||||
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
)));
|
||||||
|
|
||||||
|
return new AutoClosing(() -> {
|
||||||
|
field.set(nmsServer, old);
|
||||||
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
|
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD));
|
||||||
|
if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER));
|
||||||
|
if (end) end = !registry.containsKey(createIrisKey(LevelStem.END));
|
||||||
|
return overworld || nether || end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
|
var access = registry();
|
||||||
|
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
var source = new FlatLevelSource(settings);
|
||||||
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
|
if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD);
|
||||||
|
if (nether) register(fake, dimensions, source, LevelStem.NETHER);
|
||||||
|
if (end) register(fake, dimensions, source, LevelStem.END);
|
||||||
|
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
||||||
|
|
||||||
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
var loc = createIrisKey(key);
|
||||||
|
target.register(key, new LevelStem(
|
||||||
|
dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())),
|
||||||
|
source
|
||||||
|
), RegistrationInfo.BUILT_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.listElementIds().forEach(key -> {
|
||||||
|
var value = source.getValue(key);
|
||||||
|
var info = source.registrationInfo(key).orElse(null);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResourceLocation createIrisKey(ResourceKey<LevelStem> key) {
|
||||||
|
return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ rootProject.name = 'Iris'
|
|||||||
|
|
||||||
include(':core')
|
include(':core')
|
||||||
include(
|
include(
|
||||||
|
':nms:v1_21_R4',
|
||||||
':nms:v1_21_R3',
|
':nms:v1_21_R3',
|
||||||
':nms:v1_21_R2',
|
':nms:v1_21_R2',
|
||||||
':nms:v1_21_R1',
|
':nms:v1_21_R1',
|
||||||
|
|||||||
Reference in New Issue
Block a user