mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-01 23:47:21 +00:00
Merge branch 'dev' into feat/faster_pregen
# Conflicts: # core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java
This commit is contained in:
commit
49ce84a9e9
33
build.gradle
33
build.gradle
@ -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.5-1.20.1-1.21.4'
|
version '3.6.6-1.20.1-1.21.4'
|
||||||
|
|
||||||
// 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,6 +56,11 @@ 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_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",
|
||||||
@ -62,21 +70,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 {
|
||||||
|
@ -624,12 +624,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;
|
||||||
}
|
}
|
||||||
@ -854,13 +864,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");
|
||||||
|
@ -44,6 +44,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) {
|
||||||
@ -129,6 +130,7 @@ 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
|
||||||
@ -141,6 +143,12 @@ public class IrisSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class IrisSettingsPregen {
|
||||||
|
public boolean useVirtualThreads = false;
|
||||||
|
public int maxConcurrency = 256;
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class IrisSettingsPerformance {
|
public static class IrisSettingsPerformance {
|
||||||
public boolean trimMantleInStudio = false;
|
public boolean trimMantleInStudio = false;
|
||||||
|
@ -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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -36,6 +36,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,6 +63,7 @@ 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)
|
||||||
@ -115,6 +117,42 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
Iris.info("-------------------------");
|
Iris.info("-------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Decree(description = "Test")
|
||||||
|
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);
|
||||||
|
|
||||||
|
pw.println(Thread.activeCount() + "/" + f.size());
|
||||||
|
var run = Runtime.getRuntime();
|
||||||
|
pw.println("Memory:");
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
pw.println("========================================");
|
||||||
|
pw.println();
|
||||||
|
pw.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
pw.close();
|
||||||
|
Iris.info("DUMPED! See " + fi.getAbsolutePath());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
public void benchmarkMantle(
|
public void benchmarkMantle(
|
||||||
@Param(description = "The world to bench", aliases = {"world"})
|
@Param(description = "The world to bench", aliases = {"world"})
|
||||||
|
@ -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,7 +66,6 @@ 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;
|
||||||
@ -175,16 +172,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)
|
||||||
|
@ -39,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) {
|
||||||
@ -50,7 +52,7 @@ public class CommandPregen implements DecreeExecutor {
|
|||||||
IrisToolbelt.pregenerate(PregenTask
|
IrisToolbelt.pregenerate(PregenTask
|
||||||
.builder()
|
.builder()
|
||||||
.center(new Position2(center.getBlockX(), center.getBlockZ()))
|
.center(new Position2(center.getBlockX(), center.getBlockZ()))
|
||||||
.gui(true)
|
.gui(gui)
|
||||||
.radiusX(radius)
|
.radiusX(radius)
|
||||||
.radiusZ(radius)
|
.radiusZ(radius)
|
||||||
.build(), world);
|
.build(), world);
|
||||||
|
@ -40,6 +40,8 @@ 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.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -64,6 +66,7 @@ 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;
|
||||||
@ -96,6 +99,7 @@ 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() {
|
||||||
@ -219,10 +223,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",
|
||||||
@ -240,13 +244,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
|
||||||
@ -306,6 +313,7 @@ public class PregeneratorJob implements PregenListener {
|
|||||||
close();
|
close();
|
||||||
instance = null;
|
instance = null;
|
||||||
whenDone.forEach(Runnable::run);
|
whenDone.forEach(Runnable::run);
|
||||||
|
service.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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;
|
||||||
@ -94,6 +93,7 @@ public interface INMSBinding {
|
|||||||
throw new IllegalStateException("Missing dimenstion types to create world");
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +132,11 @@ public interface INMSBinding {
|
|||||||
|
|
||||||
AutoClosing injectLevelStems();
|
AutoClosing injectLevelStems();
|
||||||
|
|
||||||
Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end);
|
default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
|
boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,8 +126,8 @@ public class NMSBinding1X implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
return new Pair<>(0, new AutoClosing(() -> {}));
|
return injectLevelStems();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -135,6 +135,11 @@ public class NMSBinding1X implements INMSBinding {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCustomDimensions(World world) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
public CompoundTag serializeEntity(Entity location) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -31,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;
|
||||||
@ -74,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();
|
||||||
|
cachedLast = new AtomicLong(0);
|
||||||
|
cachedLastMinute = new AtomicLong(0);
|
||||||
|
totalChunks = new AtomicLong(0);
|
||||||
task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet());
|
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;
|
||||||
}
|
}
|
||||||
@ -121,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -138,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.");
|
||||||
@ -234,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
|
||||||
@ -244,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);
|
||||||
|
|
||||||
|
70
core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java
vendored
Normal file
70
core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java
vendored
Normal file
@ -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
core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java
vendored
Normal file
215
core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java
vendored
Normal file
@ -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
core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java
vendored
Normal file
48
core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java
vendored
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,14 +35,17 @@ import org.bukkit.World;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
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;
|
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 static final AtomicInteger THREAD_COUNT = new AtomicInteger();
|
||||||
private final World world;
|
private final World world;
|
||||||
private final MultiBurst burst;
|
private final ExecutorService service;
|
||||||
private final Semaphore semaphore;
|
private final Semaphore semaphore;
|
||||||
|
private final int threads;
|
||||||
private final Map<Chunk, Long> lastUse;
|
private final Map<Chunk, Long> lastUse;
|
||||||
|
|
||||||
public AsyncPregenMethod(World world, int threads) {
|
public AsyncPregenMethod(World world, int threads) {
|
||||||
@ -51,8 +54,11 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.world = world;
|
this.world = world;
|
||||||
burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
|
||||||
semaphore = new Semaphore(256);
|
Executors.newVirtualThreadPerTaskExecutor() :
|
||||||
|
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||||
|
this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
|
||||||
|
semaphore = new Semaphore(threads);
|
||||||
this.lastUse = new KMap<>();
|
this.lastUse = new KMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +112,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
semaphore.acquireUninterruptibly(256);
|
semaphore.acquireUninterruptibly(threads);
|
||||||
unloadAndSaveAllChunks();
|
unloadAndSaveAllChunks();
|
||||||
burst.close();
|
service.shutdown();
|
||||||
resetWorkerThreads();
|
resetWorkerThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +141,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
burst.complete(() -> completeChunk(x, z, listener));
|
service.submit(() -> completeChunk(x, z, listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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,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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
@ -852,6 +853,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 +892,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())) {
|
||||||
|
@ -2,12 +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 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;
|
||||||
|
|
||||||
@ -19,14 +22,9 @@ public class IrisContextInjector implements Listener {
|
|||||||
@Getter
|
@Getter
|
||||||
private static boolean missingDimensionTypes = false;
|
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();
|
||||||
@ -34,29 +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 (Bukkit.getAllowNether()) i++;
|
|
||||||
if (Bukkit.getAllowEnd()) i++;
|
|
||||||
|
|
||||||
if (INMS.get().missingDimensionTypes(overworld, nether, end)) {
|
if (INMS.get().missingDimensionTypes(overworld, nether, end)) {
|
||||||
missingDimensionTypes = true;
|
missingDimensionTypes = true;
|
||||||
totalWorlds = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overworld || nether || end) {
|
if (overworld || nether || end) {
|
||||||
var pair = INMS.get().injectUncached(overworld, nether, end);
|
autoClosing = INMS.get().injectUncached(overworld, nether, end);
|
||||||
i += pair.getA() - 3;
|
|
||||||
autoClosing = pair.getB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalWorlds = i;
|
|
||||||
instance.registerListener(this);
|
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;
|
||||||
|
@ -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;
|
||||||
@ -48,6 +49,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;
|
||||||
@ -122,11 +124,13 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
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) {
|
||||||
|
@ -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>();
|
||||||
|
|
||||||
|
@ -24,12 +24,14 @@ 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 {
|
||||||
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 +146,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() > 7000) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -645,37 +645,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -687,31 +694,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -735,7 +720,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -646,37 +646,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -688,31 +695,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -736,7 +721,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -647,37 +647,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -689,31 +696,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -737,7 +722,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -672,37 +672,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -714,31 +721,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -762,7 +747,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -676,37 +676,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new com.volmit.iris.core.nms.container.Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -718,31 +725,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().K.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -766,7 +751,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -666,37 +666,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -708,31 +715,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -756,7 +741,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -665,37 +665,44 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
public AutoClosing injectLevelStems() {
|
public AutoClosing injectLevelStems() {
|
||||||
return inject(this::supplier);
|
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
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
var reg = registry();
|
var reg = registry();
|
||||||
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end);
|
||||||
old.resources(),
|
var injected = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
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 old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
var fake = new HashMap<>(old);
|
var fake = new HashMap<>(old);
|
||||||
fake.put(Registries.LEVEL_STEM, injected);
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
field.set(reg, fake);
|
field.set(reg, fake);
|
||||||
|
|
||||||
return new Pair<>(
|
return new AutoClosing(() -> field.set(reg, old));
|
||||||
injected.size(),
|
|
||||||
new AutoClosing(() -> {
|
|
||||||
closing.close();
|
|
||||||
field.set(reg, old);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -707,31 +714,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return overworld || nether || end;
|
return overworld || nether || end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
@Override
|
||||||
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
public void removeCustomDimensions(World world) {
|
||||||
old.resources(),
|
((CraftWorld) world).getHandle().L.customDimensions = null;
|
||||||
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!");
|
|
||||||
|
|
||||||
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, transformer.apply(old));
|
|
||||||
|
|
||||||
return new AutoClosing(() -> {
|
|
||||||
field.set(nmsServer, old);
|
|
||||||
dataContextLock.unlock();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -755,7 +740,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM));
|
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<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
private void register(MappedRegistry<LevelStem> target, Registry<DimensionType> dimensions, FlatLevelSource source, ResourceKey<LevelStem> key) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user