mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-05 07:16:22 +00:00
"Folia"
This commit is contained in:
@@ -2,6 +2,7 @@ import io.github.slimjar.func.slimjarHelper
|
||||
import io.github.slimjar.resolver.data.Mirror
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import java.net.URI
|
||||
|
||||
@@ -157,6 +158,8 @@ slimJar {
|
||||
relocate("com.github.benmanes.caffeine", "$lib.caffeine")
|
||||
}
|
||||
|
||||
val embeddedAgentJar = project(":core:agent").tasks.named<Jar>("jar")
|
||||
|
||||
tasks {
|
||||
/**
|
||||
* We need parameter meta for the decree command system
|
||||
@@ -182,10 +185,14 @@ tasks {
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
dependsOn(embeddedAgentJar)
|
||||
mergeServiceFiles()
|
||||
//minimize()
|
||||
relocate("io.github.slimjar", "$lib.slimjar")
|
||||
exclude("modules/loader-agent.isolated-jar")
|
||||
from(embeddedAgentJar.map { it.archiveFile }) {
|
||||
rename { "agent.jar" }
|
||||
}
|
||||
}
|
||||
|
||||
sentryCollectSourcesJava {
|
||||
|
||||
@@ -98,8 +98,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
static {
|
||||
try {
|
||||
InstanceState.updateInstanceId();
|
||||
} catch (Throwable ignored) {
|
||||
|
||||
} catch (Throwable ex) {
|
||||
System.err.println("[Iris] Failed to update instance id: " + ex.getClass().getSimpleName()
|
||||
+ (ex.getMessage() == null ? "" : " - " + ex.getMessage()));
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,8 +137,12 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
if (slicedClass == null || i.isAnnotationPresent(slicedClass)) {
|
||||
try {
|
||||
v.add(i.getDeclaredConstructor().newInstance());
|
||||
} catch (Throwable ignored) {
|
||||
|
||||
} catch (Throwable ex) {
|
||||
Iris.warn("Skipped class initialization for %s: %s%s",
|
||||
i.getName(),
|
||||
ex.getClass().getSimpleName(),
|
||||
ex.getMessage() == null ? "" : " - " + ex.getMessage());
|
||||
Iris.reportError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +158,12 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
if (slicedClass == null || i.isAnnotationPresent(slicedClass)) {
|
||||
try {
|
||||
v.add(i);
|
||||
} catch (Throwable ignored) {
|
||||
|
||||
} catch (Throwable ex) {
|
||||
Iris.warn("Skipped class discovery entry for %s: %s%s",
|
||||
i.getName(),
|
||||
ex.getClass().getSimpleName(),
|
||||
ex.getMessage() == null ? "" : " - " + ex.getMessage());
|
||||
Iris.reportError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,8 +191,9 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
instance.getLogger().info(instance.getTag() + string.replaceAll("(<([^>]+)>)", ""));
|
||||
} catch (Throwable ignored1) {
|
||||
|
||||
} catch (Throwable inner) {
|
||||
System.err.println("[Iris] Failed to emit log message: " + inner.getMessage());
|
||||
inner.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,8 +318,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void later(NastyRunnable object) {
|
||||
try {
|
||||
Bukkit.getScheduler().scheduleAsyncDelayedTask(instance, () ->
|
||||
{
|
||||
J.a(() -> {
|
||||
try {
|
||||
object.run();
|
||||
} catch (Throwable e) {
|
||||
@@ -316,8 +326,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}, RNG.r.i(100, 1200));
|
||||
} catch (IllegalPluginAccessException ignored) {
|
||||
|
||||
} catch (IllegalPluginAccessException ex) {
|
||||
Iris.verbose("Skipping deferred task registration because plugin access is unavailable: "
|
||||
+ ex.getClass().getSimpleName()
|
||||
+ (ex.getMessage() == null ? "" : " - " + ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,7 +488,12 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
|
||||
public void addShutdownHook() {
|
||||
if (shutdownHook != null) {
|
||||
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||
try {
|
||||
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||
} catch (IllegalStateException ex) {
|
||||
Iris.debug("Skipping shutdown hook replacement because JVM shutdown is already in progress.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
shutdownHook = new Thread(() -> {
|
||||
Bukkit.getWorlds()
|
||||
@@ -487,9 +504,15 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
|
||||
MultiBurst.burst.close();
|
||||
MultiBurst.ioBurst.close();
|
||||
services.clear();
|
||||
});
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
if (services != null) {
|
||||
services.clear();
|
||||
}
|
||||
}, "Iris-ShutdownHook");
|
||||
try {
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
} catch (IllegalStateException ex) {
|
||||
Iris.debug("Skipping shutdown hook registration because JVM shutdown is already in progress.");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkForBukkitWorlds(Predicate<String> filter) {
|
||||
@@ -563,12 +586,14 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
|
||||
public void onDisable() {
|
||||
if (IrisSafeguard.isForceShutdown()) return;
|
||||
services.values().forEach(IrisService::onDisable);
|
||||
if (services != null) {
|
||||
services.values().forEach(IrisService::onDisable);
|
||||
}
|
||||
if (configHotloadEngine != null) {
|
||||
configHotloadEngine.clear();
|
||||
configHotloadEngine = null;
|
||||
}
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
J.cancelPluginTasks();
|
||||
HandlerList.unregisterAll((Plugin) this);
|
||||
postShutdown.forEach(Runnable::run);
|
||||
super.onDisable();
|
||||
@@ -632,7 +657,12 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
|
||||
try {
|
||||
return IO.readAll(file);
|
||||
} catch (Throwable ignored) {
|
||||
} catch (Throwable ex) {
|
||||
Iris.warn("Failed to read settings file %s: %s%s",
|
||||
file.getAbsolutePath(),
|
||||
ex.getClass().getSimpleName(),
|
||||
ex.getMessage() == null ? "" : " - " + ex.getMessage());
|
||||
Iris.reportError(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -765,7 +795,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
JsonObject json = JsonParser.parseReader(r).getAsJsonObject();
|
||||
if (json.has("version"))
|
||||
version = json.get("version").getAsString();
|
||||
} catch (IOException | JsonParseException ignored) {
|
||||
} catch (IOException | JsonParseException ex) {
|
||||
Iris.verbose("Failed to read dimension version metadata for " + dimName + ": "
|
||||
+ ex.getClass().getSimpleName()
|
||||
+ (ex.getMessage() == null ? "" : " - " + ex.getMessage()));
|
||||
}
|
||||
Iris.info(" " + dimName + " v" + version);
|
||||
}
|
||||
|
||||
@@ -207,7 +207,6 @@ public class IrisSettings {
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsGeneral {
|
||||
public boolean DoomsdayAnnihilationSelfDestructMode = false;
|
||||
public boolean commandSounds = true;
|
||||
public boolean debug = false;
|
||||
public boolean dumpMantleOnError = false;
|
||||
|
||||
@@ -104,7 +104,13 @@ public class ServerConfigurator {
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(boolean fullInstall) {
|
||||
return installDataPacks(DataVersion.getDefault(), fullInstall);
|
||||
IDataFixer fixer = DataVersion.getDefault();
|
||||
if (fixer == null) {
|
||||
DataVersion fallback = DataVersion.getLatest();
|
||||
Iris.warn("Primary datapack fixer was null, forcing latest fixer: " + fallback.getVersion());
|
||||
fixer = fallback.get();
|
||||
}
|
||||
return installDataPacks(fixer, fullInstall);
|
||||
}
|
||||
|
||||
public static boolean installDataPacks(IDataFixer fixer, boolean fullInstall) {
|
||||
|
||||
@@ -39,7 +39,6 @@ import art.arcane.volmlib.util.io.IO;
|
||||
import art.arcane.iris.util.misc.ServerProperties;
|
||||
import art.arcane.iris.util.plugin.VolmitSender;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import lombok.SneakyThrows;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
@@ -120,6 +119,13 @@ public class CommandIris implements DecreeExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (J.isFolia()) {
|
||||
if (stageFoliaWorldCreation(name, dimension, seed, main)) {
|
||||
sender().sendMessage(C.GREEN + "World staging completed. Restart the server to generate/load \"" + name + "\".");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
worldCreation = true;
|
||||
IrisToolbelt.createWorld()
|
||||
@@ -147,20 +153,98 @@ public class CommandIris implements DecreeExecutor {
|
||||
if (main) sender().sendMessage(C.GREEN + "Your world will automatically be set as the main world when the server restarts.");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void updateMainWorld(String newName) {
|
||||
File worlds = Bukkit.getWorldContainer();
|
||||
var data = ServerProperties.DATA;
|
||||
try (var in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) {
|
||||
data.load(in);
|
||||
private boolean updateMainWorld(String newName) {
|
||||
try {
|
||||
File worlds = Bukkit.getWorldContainer();
|
||||
var data = ServerProperties.DATA;
|
||||
try (var in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) {
|
||||
data.load(in);
|
||||
}
|
||||
|
||||
File oldWorldFolder = new File(worlds, ServerProperties.LEVEL_NAME);
|
||||
File newWorldFolder = new File(worlds, newName);
|
||||
if (!newWorldFolder.exists() && !newWorldFolder.mkdirs()) {
|
||||
Iris.warn("Could not create target main world folder: " + newWorldFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
for (String sub : List.of("datapacks", "playerdata", "advancements", "stats")) {
|
||||
File source = new File(oldWorldFolder, sub);
|
||||
if (!source.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IO.copyDirectory(source.toPath(), new File(newWorldFolder, sub).toPath());
|
||||
}
|
||||
|
||||
data.setProperty("level-name", newName);
|
||||
try (var out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) {
|
||||
data.store(out, null);
|
||||
}
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to update server.properties main world to \"" + newName + "\"");
|
||||
Iris.reportError(e);
|
||||
return false;
|
||||
}
|
||||
for (String sub : List.of("datapacks", "playerdata", "advancements", "stats")) {
|
||||
IO.copyDirectory(new File(worlds, ServerProperties.LEVEL_NAME + "/" + sub).toPath(), new File(worlds, newName + "/" + sub).toPath());
|
||||
}
|
||||
|
||||
private boolean stageFoliaWorldCreation(String name, IrisDimension dimension, long seed, boolean main) {
|
||||
sender().sendMessage(C.YELLOW + "Runtime world creation is disabled on Folia.");
|
||||
sender().sendMessage(C.YELLOW + "Preparing world files and bukkit.yml for next startup...");
|
||||
|
||||
File worldFolder = new File(Bukkit.getWorldContainer(), name);
|
||||
IrisDimension installed = Iris.service(StudioSVC.class).installIntoWorld(sender(), dimension.getLoadKey(), worldFolder);
|
||||
if (installed == null) {
|
||||
sender().sendMessage(C.RED + "Failed to stage world files for dimension \"" + dimension.getLoadKey() + "\".");
|
||||
return false;
|
||||
}
|
||||
|
||||
data.setProperty("level-name", newName);
|
||||
try (var out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) {
|
||||
data.store(out, null);
|
||||
if (!registerWorldInBukkitYml(name, dimension.getLoadKey(), seed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (main) {
|
||||
if (updateMainWorld(name)) {
|
||||
sender().sendMessage(C.GREEN + "Updated server.properties level-name to \"" + name + "\".");
|
||||
} else {
|
||||
sender().sendMessage(C.RED + "World was staged, but failed to update server.properties main world.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sender().sendMessage(C.GREEN + "Staged Iris world \"" + name + "\" with generator Iris:" + dimension.getLoadKey() + " and seed " + seed + ".");
|
||||
if (main) {
|
||||
sender().sendMessage(C.GREEN + "This world is now configured as main for next restart.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean registerWorldInBukkitYml(String worldName, String dimension, Long seed) {
|
||||
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
|
||||
ConfigurationSection worlds = yml.getConfigurationSection("worlds");
|
||||
if (worlds == null) {
|
||||
worlds = yml.createSection("worlds");
|
||||
}
|
||||
ConfigurationSection worldSection = worlds.getConfigurationSection(worldName);
|
||||
if (worldSection == null) {
|
||||
worldSection = worlds.createSection(worldName);
|
||||
}
|
||||
|
||||
String generator = "Iris:" + dimension;
|
||||
worldSection.set("generator", generator);
|
||||
if (seed != null) {
|
||||
worldSection.set("seed", seed);
|
||||
}
|
||||
|
||||
try {
|
||||
yml.save(BUKKIT_YML);
|
||||
Iris.info("Registered \"" + worldName + "\" in bukkit.yml");
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
sender().sendMessage(C.RED + "Failed to update bukkit.yml: " + e.getMessage());
|
||||
Iris.error("Failed to update bukkit.yml!");
|
||||
Iris.reportError(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,22 +523,23 @@ public class CommandIris implements DecreeExecutor {
|
||||
sender().sendMessage(C.GOLD + world + " is not an iris world.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dimension == null) {
|
||||
sender().sendMessage(C.RED + "Could not determine Iris dimension for " + world + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
sender().sendMessage(C.GREEN + "Loading world: " + world);
|
||||
|
||||
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
|
||||
String gen = "Iris:" + dimension;
|
||||
ConfigurationSection section = yml.contains("worlds") ? yml.getConfigurationSection("worlds") : yml.createSection("worlds");
|
||||
if (!section.contains(world)) {
|
||||
section.createSection(world).set("generator", gen);
|
||||
try {
|
||||
yml.save(BUKKIT_YML);
|
||||
Iris.info("Registered \"" + world + "\" in bukkit.yml");
|
||||
} catch (IOException e) {
|
||||
Iris.error("Failed to update bukkit.yml!");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
if (!registerWorldInBukkitYml(world, dimension, null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (J.isFolia()) {
|
||||
sender().sendMessage(C.YELLOW + "Folia cannot load new worlds at runtime. Restart the server to load \"" + world + "\".");
|
||||
return;
|
||||
}
|
||||
|
||||
Iris.instance.checkForBukkitWorlds(world::equals);
|
||||
sender().sendMessage(C.GREEN + world + " loaded successfully.");
|
||||
}
|
||||
@@ -498,7 +583,12 @@ public class CommandIris implements DecreeExecutor {
|
||||
for (String key : data.getDimensionLoader().getPossibleKeys()) {
|
||||
options.add(key);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
} catch (Throwable ex) {
|
||||
Iris.warn("Failed to read dimension keys from pack %s: %s%s",
|
||||
pack.getName(),
|
||||
ex.getClass().getSimpleName(),
|
||||
ex.getMessage() == null ? "" : " - " + ex.getMessage());
|
||||
Iris.reportError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,13 @@ public class CommandStudio implements DecreeExecutor {
|
||||
IrisDimension dimension,
|
||||
@Param(defaultValue = "1337", description = "The seed to generate the studio with", aliases = "s")
|
||||
long seed) {
|
||||
if (J.isFolia()) {
|
||||
sender().sendMessage(C.RED + "Studio world opening is disabled on Folia.");
|
||||
sender().sendMessage(C.YELLOW + "Folia does not currently support runtime world creation via Bukkit.createWorld().");
|
||||
sender().sendMessage(C.YELLOW + "Use Paper/Purpur for Studio mode, or preconfigure worlds and restart.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender().sendMessage(C.GREEN + "Opening studio for the \"" + dimension.getName() + "\" pack (seed: " + seed + ")");
|
||||
Iris.service(StudioSVC.class).open(sender(), seed, dimension.getLoadKey());
|
||||
}
|
||||
@@ -329,10 +336,10 @@ public class CommandStudio implements DecreeExecutor {
|
||||
var player = player();
|
||||
var engine = engine();
|
||||
|
||||
ta.set(Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () ->
|
||||
ta.set(J.sr(() ->
|
||||
{
|
||||
if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
|
||||
Bukkit.getScheduler().cancelTask(ta.get());
|
||||
J.csr(ta.get());
|
||||
sender.sendMessage(C.GREEN + "Opened inventory!");
|
||||
return;
|
||||
}
|
||||
@@ -342,7 +349,7 @@ public class CommandStudio implements DecreeExecutor {
|
||||
}
|
||||
|
||||
engine.addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player.getWorld(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ(), 1);
|
||||
}, 0, fast ? 5 : 35));
|
||||
}, fast ? 5 : 35));
|
||||
|
||||
sender().sendMessage(C.GREEN + "Opening inventory now!");
|
||||
player().openInventory(inv);
|
||||
|
||||
@@ -93,7 +93,9 @@ public class INMS {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (ClassNotFoundException|NoClassDefFoundError classNotFoundException) {}
|
||||
} catch (ClassNotFoundException|NoClassDefFoundError classNotFoundException) {
|
||||
Iris.warn("Failed to load NMS binding class for " + code + ": " + classNotFoundException.getMessage());
|
||||
}
|
||||
|
||||
Iris.info("Craftbukkit " + code + " <-> " + NMSBinding1X.class.getSimpleName() + " Successfully Bound");
|
||||
Iris.warn("Note: Some features of Iris may not work the same since you are on an unsupported version of Minecraft.");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package art.arcane.iris.core.nms.datapack;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.nms.INMS;
|
||||
import art.arcane.iris.core.nms.datapack.v1192.DataFixerV1192;
|
||||
import art.arcane.iris.core.nms.datapack.v1206.DataFixerV1206;
|
||||
@@ -36,7 +37,21 @@ public enum DataVersion {
|
||||
}
|
||||
|
||||
public static IDataFixer getDefault() {
|
||||
return INMS.get().getDataVersion().get();
|
||||
DataVersion version = INMS.get().getDataVersion();
|
||||
if (version == null || version == UNSUPPORTED) {
|
||||
DataVersion fallback = getLatest();
|
||||
Iris.warn("Unsupported datapack version mapping detected, falling back to latest fixer: " + fallback.getVersion());
|
||||
return fallback.get();
|
||||
}
|
||||
|
||||
IDataFixer fixer = version.get();
|
||||
if (fixer == null) {
|
||||
DataVersion fallback = getLatest();
|
||||
Iris.warn("Null datapack fixer for " + version.getVersion() + ", falling back to latest fixer: " + fallback.getVersion());
|
||||
return fallback.get();
|
||||
}
|
||||
|
||||
return fixer;
|
||||
}
|
||||
|
||||
public static DataVersion getLatest() {
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
@@ -235,16 +234,13 @@ public class DeepSearchPregenerator extends Thread implements Listener {
|
||||
}
|
||||
save();
|
||||
jobs.remove(world.getName());
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (deepFile.exists()){
|
||||
deepFile.delete();
|
||||
J.sleep(1000);
|
||||
}
|
||||
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||
J.a(() -> {
|
||||
while (deepFile.exists()) {
|
||||
deepFile.delete();
|
||||
J.sleep(1000);
|
||||
}
|
||||
}.runTaskLater(Iris.instance, 20L);
|
||||
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||
}, 20);
|
||||
} catch (Exception e) {
|
||||
Iris.error("Failed to shutdown DeepSearch for " + world.getName());
|
||||
e.printStackTrace();
|
||||
@@ -271,4 +267,3 @@ public class DeepSearchPregenerator extends Thread implements Listener {
|
||||
boolean paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -165,7 +164,10 @@ public class LazyPregenerator extends Thread implements Listener {
|
||||
}
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException ignored) {}
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
Iris.verbose("Lazy pregenerator worker interrupted while waiting for chunk " + chunk + ".");
|
||||
}
|
||||
lazyGeneratedChunks.addAndGet(1);
|
||||
});
|
||||
}
|
||||
@@ -257,16 +259,13 @@ public class LazyPregenerator extends Thread implements Listener {
|
||||
}
|
||||
save();
|
||||
jobs.remove(world.getName());
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (lazyFile.exists()){
|
||||
lazyFile.delete();
|
||||
J.sleep(1000);
|
||||
}
|
||||
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||
J.a(() -> {
|
||||
while (lazyFile.exists()) {
|
||||
lazyFile.delete();
|
||||
J.sleep(1000);
|
||||
}
|
||||
}.runTaskLater(Iris.instance, 20L);
|
||||
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||
}, 20);
|
||||
} catch (Exception e) {
|
||||
Iris.error("Failed to shutdown Lazygen for " + world.getName());
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.checkerframework.checker.units.qual.N;
|
||||
|
||||
import java.io.File;
|
||||
@@ -233,7 +232,9 @@ public class TurboPregenerator extends Thread implements Listener {
|
||||
});
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
Iris.verbose("Turbo pregenerator worker interrupted while waiting for chunk " + chunk + ".");
|
||||
}
|
||||
turboGeneratedChunks.addAndGet(1);
|
||||
});
|
||||
@@ -321,16 +322,13 @@ public class TurboPregenerator extends Thread implements Listener {
|
||||
}
|
||||
save();
|
||||
jobs.remove(world.getName());
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (turboFile.exists()) {
|
||||
turboFile.delete();
|
||||
J.sleep(1000);
|
||||
}
|
||||
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||
J.a(() -> {
|
||||
while (turboFile.exists()) {
|
||||
turboFile.delete();
|
||||
J.sleep(1000);
|
||||
}
|
||||
}.runTaskLater(Iris.instance, 20L);
|
||||
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||
}, 20);
|
||||
} catch (Exception e) {
|
||||
Iris.error("Failed to shutdown turbogen for " + world.getName());
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -23,7 +23,6 @@ import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.tools.IrisToolbelt;
|
||||
import art.arcane.volmlib.util.board.Board;
|
||||
import art.arcane.volmlib.util.board.BoardManager;
|
||||
import art.arcane.volmlib.util.board.BoardProvider;
|
||||
import art.arcane.volmlib.util.board.BoardSettings;
|
||||
import art.arcane.volmlib.util.board.ScoreDirection;
|
||||
@@ -43,40 +42,43 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BoardSVC implements IrisService, BoardProvider {
|
||||
private final KMap<Player, PlayerBoard> boards = new KMap<>();
|
||||
private ScheduledExecutorService executor;
|
||||
private BoardManager<Board> manager;
|
||||
private BoardSettings settings;
|
||||
private boolean boardEnabled;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
executor = Executors.newScheduledThreadPool(0, Thread.ofVirtual().factory());
|
||||
manager = new BoardManager<>(Iris.instance, BoardSettings.builder()
|
||||
boardEnabled = true;
|
||||
settings = BoardSettings.builder()
|
||||
.boardProvider(this)
|
||||
.scoreDirection(ScoreDirection.DOWN)
|
||||
.build(), Board::new);
|
||||
.build();
|
||||
|
||||
for (Player player : Iris.instance.getServer().getOnlinePlayers()) {
|
||||
J.runEntity(player, () -> updatePlayer(player));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
executor.shutdownNow();
|
||||
manager.onDisable();
|
||||
boardEnabled = false;
|
||||
for (PlayerBoard board : new ArrayList<>(boards.values())) {
|
||||
board.cancel();
|
||||
}
|
||||
boards.clear();
|
||||
settings = null;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(PlayerChangedWorldEvent e) {
|
||||
J.s(() -> updatePlayer(e.getPlayer()));
|
||||
J.runEntity(e.getPlayer(), () -> updatePlayer(e.getPlayer()));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(PlayerJoinEvent e) {
|
||||
J.s(() -> updatePlayer(e.getPlayer()));
|
||||
J.runEntity(e.getPlayer(), () -> updatePlayer(e.getPlayer()));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -85,16 +87,34 @@ public class BoardSVC implements IrisService, BoardProvider {
|
||||
}
|
||||
|
||||
public void updatePlayer(Player p) {
|
||||
if (!boardEnabled || settings == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!J.isOwnedByCurrentRegion(p)) {
|
||||
J.runEntity(p, () -> updatePlayer(p));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IrisToolbelt.isIrisStudioWorld(p.getWorld())) {
|
||||
manager.remove(p);
|
||||
manager.setup(p);
|
||||
boards.computeIfAbsent(p, PlayerBoard::new);
|
||||
} else remove(p);
|
||||
}
|
||||
|
||||
private void remove(Player player) {
|
||||
manager.remove(player);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!J.isOwnedByCurrentRegion(player)) {
|
||||
J.runEntity(player, () -> remove(player));
|
||||
return;
|
||||
}
|
||||
|
||||
var board = boards.remove(player);
|
||||
if (board != null) board.task.cancel(true);
|
||||
if (board != null) {
|
||||
board.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,27 +124,60 @@ public class BoardSVC implements IrisService, BoardProvider {
|
||||
|
||||
@Override
|
||||
public List<String> getLines(Player player) {
|
||||
return boards.computeIfAbsent(player, PlayerBoard::new).lines;
|
||||
PlayerBoard board = boards.get(player);
|
||||
if (board == null) {
|
||||
return List.of();
|
||||
}
|
||||
return board.lines;
|
||||
}
|
||||
|
||||
@Data
|
||||
public class PlayerBoard {
|
||||
private final Player player;
|
||||
private final ScheduledFuture<?> task;
|
||||
private final Board board;
|
||||
private volatile List<String> lines;
|
||||
private volatile boolean cancelled;
|
||||
|
||||
public PlayerBoard(Player player) {
|
||||
this.player = player;
|
||||
this.board = new Board(player, settings);
|
||||
this.lines = new ArrayList<>();
|
||||
this.task = executor.scheduleAtFixedRate(this::tick, 0, 1, TimeUnit.SECONDS);
|
||||
this.cancelled = false;
|
||||
schedule(0);
|
||||
}
|
||||
|
||||
private void schedule(int delayTicks) {
|
||||
if (cancelled || !boardEnabled || !player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
J.runEntity(player, this::tick, delayTicks);
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
if (cancelled || !boardEnabled || !player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IrisToolbelt.isIrisStudioWorld(player.getWorld())) {
|
||||
boards.remove(player);
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
||||
board.update();
|
||||
schedule(20);
|
||||
return;
|
||||
}
|
||||
|
||||
update();
|
||||
board.update();
|
||||
schedule(20);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
cancelled = true;
|
||||
J.runEntity(player, board::remove);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
|
||||
@@ -24,7 +24,7 @@ import art.arcane.iris.core.edit.BukkitBlockEditor;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.volmlib.util.math.M;
|
||||
import art.arcane.iris.util.plugin.IrisService;
|
||||
import org.bukkit.Bukkit;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@@ -33,16 +33,21 @@ import org.bukkit.event.world.WorldUnloadEvent;
|
||||
|
||||
public class EditSVC implements IrisService {
|
||||
private KMap<World, BlockEditor> editors;
|
||||
private int updateTaskId = -1;
|
||||
public static boolean deletingWorld = false;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.editors = new KMap<>();
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::update, 1000, 1000);
|
||||
updateTaskId = J.sr(this::update, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (updateTaskId != -1) {
|
||||
J.csr(updateTaskId);
|
||||
updateTaskId = -1;
|
||||
}
|
||||
flushNow();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -231,6 +232,20 @@ public class IrisEngineSVC implements IrisService {
|
||||
updateTicker.start();
|
||||
}
|
||||
|
||||
private static boolean isMantleClosed(Throwable throwable) {
|
||||
Throwable current = throwable;
|
||||
while (current != null) {
|
||||
String message = current.getMessage();
|
||||
if (message != null && message.toLowerCase(Locale.ROOT).contains("mantle is closed")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
current = current.getCause();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private final class Registered {
|
||||
private final String name;
|
||||
private final PlatformChunkGenerator access;
|
||||
@@ -261,12 +276,19 @@ public class IrisEngineSVC implements IrisService {
|
||||
if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) {
|
||||
trimmer = service.scheduleAtFixedRate(() -> {
|
||||
Engine engine = getEngine();
|
||||
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
if (engine == null
|
||||
|| engine.isClosed()
|
||||
|| engine.getMantle().getMantle().isClosed()
|
||||
|| !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
return;
|
||||
|
||||
try {
|
||||
engine.getMantle().trim(tectonicLimit());
|
||||
} catch (Throwable e) {
|
||||
if (isMantleClosed(e)) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
Iris.reportError(e);
|
||||
Iris.error("EngineSVC: Failed to trim for " + name);
|
||||
e.printStackTrace();
|
||||
@@ -277,7 +299,10 @@ public class IrisEngineSVC implements IrisService {
|
||||
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
||||
unloader = service.scheduleAtFixedRate(() -> {
|
||||
Engine engine = getEngine();
|
||||
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
if (engine == null
|
||||
|| engine.isClosed()
|
||||
|| engine.getMantle().getMantle().isClosed()
|
||||
|| !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
return;
|
||||
|
||||
try {
|
||||
@@ -287,6 +312,10 @@ public class IrisEngineSVC implements IrisService {
|
||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (isMantleClosed(e)) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
Iris.reportError(e);
|
||||
Iris.error("EngineSVC: Failed to unload for " + name);
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -22,7 +22,6 @@ import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.util.plugin.IrisService;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
@@ -71,7 +70,7 @@ public class ObjectSVC implements IrisService {
|
||||
*/
|
||||
private void revert(Map<Block, BlockData> blocks) {
|
||||
Iterator<Map.Entry<Block, BlockData>> it = blocks.entrySet().iterator();
|
||||
Bukkit.getScheduler().runTask(Iris.instance, () -> {
|
||||
J.s(() -> {
|
||||
int amount = 0;
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<Block, BlockData> entry = it.next();
|
||||
@@ -88,4 +87,4 @@ public class ObjectSVC implements IrisService {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,8 +341,8 @@ public class StudioSVC implements IrisService {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Iris.reportError(e);
|
||||
sender.sendMessage("Error when creating studio world:");
|
||||
e.printStackTrace();
|
||||
sender.sendMessage("Failed to open studio world: " + e.getMessage());
|
||||
Iris.error("Studio world creation failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,10 @@ public class IrisCreator {
|
||||
throw new IrisException("You cannot invoke create() on the main thread.");
|
||||
}
|
||||
|
||||
if (J.isFolia()) {
|
||||
throw new IrisException("Folia does not support runtime world creation via Bukkit.createWorld(). Configure worlds before startup and restart the server.");
|
||||
}
|
||||
|
||||
IrisDimension d = IrisToolbelt.getDimension(dimension());
|
||||
|
||||
if (d == null) {
|
||||
@@ -171,6 +175,9 @@ public class IrisCreator {
|
||||
world = J.sfut(() -> INMS.get().createWorld(wc)).get();
|
||||
} catch (Throwable e) {
|
||||
done.set(true);
|
||||
if (containsCreateWorldUnsupportedOperation(e)) {
|
||||
throw new IrisException("Runtime world creation is not supported on this server variant. Configure worlds before startup and restart the server.", e);
|
||||
}
|
||||
throw new IrisException("Failed to create world!", e);
|
||||
}
|
||||
|
||||
@@ -226,6 +233,22 @@ public class IrisCreator {
|
||||
return world;
|
||||
}
|
||||
|
||||
private static boolean containsCreateWorldUnsupportedOperation(Throwable throwable) {
|
||||
Throwable cursor = throwable;
|
||||
while (cursor != null) {
|
||||
if (cursor instanceof UnsupportedOperationException) {
|
||||
for (StackTraceElement element : cursor.getStackTrace()) {
|
||||
if ("org.bukkit.craftbukkit.CraftServer".equals(element.getClassName())
|
||||
&& "createWorld".equals(element.getMethodName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor = cursor.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addToBukkitYml() {
|
||||
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
|
||||
String gen = "Iris:" + dimension;
|
||||
|
||||
@@ -62,7 +62,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -192,52 +191,94 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
private void discoverChunks() {
|
||||
var mantle = getEngine().getMantle().getMantle();
|
||||
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
|
||||
int r = 1;
|
||||
|
||||
for (int x = -r; x <= r; x++) {
|
||||
for (int z = -r; z <= r; z++) {
|
||||
mantle.getChunk(i.getLocation().getChunk()).flag(MantleFlag.DISCOVERED, true);
|
||||
}
|
||||
}
|
||||
World world = getEngine().getWorld().realWorld();
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
J.s(() -> {
|
||||
for (Player player : world.getPlayers()) {
|
||||
if (player == null || !player.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
J.runEntity(player, () -> {
|
||||
int centerX = player.getLocation().getBlockX() >> 4;
|
||||
int centerZ = player.getLocation().getBlockZ() >> 4;
|
||||
int radius = 1;
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
mantle.getChunk(centerX + x, centerZ + z).flag(MantleFlag.DISCOVERED, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateChunks() {
|
||||
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
|
||||
int r = 1;
|
||||
World world = getEngine().getWorld().realWorld();
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Chunk c = i.getLocation().getChunk();
|
||||
for (int x = -r; x <= r; x++) {
|
||||
for (int z = -r; z <= r; z++) {
|
||||
if (c.getWorld().isChunkLoaded(c.getX() + x, c.getZ() + z) && Chunks.isSafe(getEngine().getWorld().realWorld(), c.getX() + x, c.getZ() + z)) {
|
||||
J.s(() -> {
|
||||
for (Player player : world.getPlayers()) {
|
||||
if (player == null || !player.isOnline()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IrisSettings.get().getWorld().isPostLoadBlockUpdates()) {
|
||||
getEngine().updateChunk(c.getWorld().getChunkAt(c.getX() + x, c.getZ() + z));
|
||||
}
|
||||
J.runEntity(player, () -> {
|
||||
int centerX = player.getLocation().getBlockX() >> 4;
|
||||
int centerZ = player.getLocation().getBlockZ() >> 4;
|
||||
int radius = 1;
|
||||
|
||||
if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
|
||||
Chunk cx = getEngine().getWorld().realWorld().getChunkAt(c.getX() + x, c.getZ() + z);
|
||||
int finalX = c.getX() + x;
|
||||
int finalZ = c.getZ() + z;
|
||||
J.a(() -> getMantle().raiseFlag(finalX, finalZ, MantleFlag.INITIAL_SPAWNED_MARKER,
|
||||
() -> {
|
||||
J.a(() -> spawnIn(cx, true), RNG.r.i(5, 200));
|
||||
getSpawnersFromMarkers(cx).forEach((blockf, spawners) -> {
|
||||
if (spawners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
|
||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||
spawn(block, s, true);
|
||||
});
|
||||
}));
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
int targetX = centerX + x;
|
||||
int targetZ = centerZ + z;
|
||||
J.runRegion(world, targetX, targetZ, () -> updateChunkRegion(world, targetX, targetZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateChunkRegion(World world, int chunkX, int chunkZ) {
|
||||
if (world == null || !world.isChunkLoaded(chunkX, chunkZ) || !Chunks.isSafe(world, chunkX, chunkZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Chunk chunk = world.getChunkAt(chunkX, chunkZ);
|
||||
|
||||
if (IrisSettings.get().getWorld().isPostLoadBlockUpdates()) {
|
||||
getEngine().updateChunk(chunk);
|
||||
}
|
||||
|
||||
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
getMantle().raiseFlag(chunkX, chunkZ, MantleFlag.INITIAL_SPAWNED_MARKER, () -> {
|
||||
int delay = RNG.r.i(5, 200);
|
||||
J.runRegion(world, chunkX, chunkZ, () -> {
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||
return;
|
||||
}
|
||||
spawnIn(world.getChunkAt(chunkX, chunkZ), true);
|
||||
}, delay);
|
||||
|
||||
getSpawnersFromMarkers(chunk).forEach((blockf, spawners) -> {
|
||||
if (spawners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
|
||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||
spawn(block, s, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private boolean onAsyncTick() {
|
||||
@@ -274,8 +315,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
|
||||
int spawnBuffer = RNG.r.i(2, 12);
|
||||
World world = getEngine().getWorld().realWorld();
|
||||
if (world == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Chunk[] cc = getEngine().getWorld().realWorld().getLoadedChunks();
|
||||
Chunk[] cc = getLoadedChunksSnapshot(world);
|
||||
while (spawnBuffer-- > 0) {
|
||||
if (cc.length == 0) {
|
||||
Iris.debug("Can't spawn. No chunks!");
|
||||
@@ -283,18 +328,60 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
|
||||
Chunk c = cc[RNG.r.nextInt(cc.length)];
|
||||
|
||||
if (!c.isLoaded() || !Chunks.isSafe(c.getWorld(), c.getX(), c.getZ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spawnIn(c, false);
|
||||
spawnChunkSafely(world, c.getX(), c.getZ(), false);
|
||||
}
|
||||
|
||||
energy -= (actuallySpawned / 2D);
|
||||
return actuallySpawned > 0;
|
||||
}
|
||||
|
||||
private Chunk[] getLoadedChunksSnapshot(World world) {
|
||||
if (world == null) {
|
||||
return new Chunk[0];
|
||||
}
|
||||
|
||||
CompletableFuture<Chunk[]> future = new CompletableFuture<>();
|
||||
J.s(() -> {
|
||||
try {
|
||||
future.complete(world.getLoadedChunks());
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return future.get(2, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
return new Chunk[0];
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnChunkSafely(World world, int chunkX, int chunkZ, boolean initial) {
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
J.runRegion(world, chunkX, chunkZ, () -> {
|
||||
try {
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ) || !Chunks.isSafe(world, chunkX, chunkZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
spawnIn(world.getChunkAt(chunkX, chunkZ), initial);
|
||||
} finally {
|
||||
future.complete(null);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
future.get(5, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fixEnergy() {
|
||||
energy = M.clip(energy, 1D, getDimension().getMaximumEnergy());
|
||||
}
|
||||
@@ -317,7 +404,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
|
||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||
spawn(block, s, false);
|
||||
J.a(() -> getMantle().raiseFlag(c.getX(), c.getZ(), MantleFlag.INITIAL_SPAWNED_MARKER,
|
||||
J.runRegion(c.getWorld(), c.getX(), c.getZ(), () -> getMantle().raiseFlag(c.getX(), c.getZ(), MantleFlag.INITIAL_SPAWNED_MARKER,
|
||||
() -> spawn(block, s, true)));
|
||||
});
|
||||
}
|
||||
@@ -359,7 +446,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
try {
|
||||
spawn(c, v);
|
||||
} catch (Throwable e) {
|
||||
J.s(() -> spawn(c, v));
|
||||
J.runRegion(c.getWorld(), c.getX(), c.getZ(), () -> spawn(c, v));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,8 +624,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
public void execute(Future<Chunk> chunkFuture) {
|
||||
try {
|
||||
chunkFuture.get();
|
||||
} catch (InterruptedException | ExecutionException ignored) {
|
||||
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
Iris.verbose("Chunk warmup interrupted while loading async teleport chunk.");
|
||||
} catch (ExecutionException ex) {
|
||||
Iris.reportError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,19 +652,10 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
IrisPosition pos = new IrisPosition((c.getX() << 4) + x, y, (c.getZ() << 4) + z);
|
||||
|
||||
if (mark.isEmptyAbove()) {
|
||||
AtomicBoolean remove = new AtomicBoolean(false);
|
||||
boolean remove = c.getBlock(x, y + 1, z).getBlockData().getMaterial().isSolid()
|
||||
|| c.getBlock(x, y + 2, z).getBlockData().getMaterial().isSolid();
|
||||
|
||||
try {
|
||||
J.sfut(() -> {
|
||||
if (c.getBlock(x, y + 1, z).getBlockData().getMaterial().isSolid() || c.getBlock(x, y + 2, z).getBlockData().getMaterial().isSolid()) {
|
||||
remove.set(true);
|
||||
}
|
||||
}).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (remove.get()) {
|
||||
if (remove) {
|
||||
b.add(pos);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -297,20 +297,20 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
try {
|
||||
Semaphore semaphore = new Semaphore(1024);
|
||||
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
|
||||
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> {
|
||||
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, c, () -> {
|
||||
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
|
||||
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
|
||||
if (!TileData.setTileState(block, v.getData()))
|
||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey());
|
||||
});
|
||||
}, 0));
|
||||
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, () -> {
|
||||
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, c, () -> {
|
||||
chunk.iterate(Identifier.class, (x, y, z, v) -> {
|
||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||
});
|
||||
}, 0));
|
||||
|
||||
chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, () -> {
|
||||
chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, c, () -> {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
int[][] grid = new int[16][16];
|
||||
for (int x = 0; x < 16; x++) {
|
||||
@@ -362,19 +362,22 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
return;
|
||||
|
||||
for (var script : scripts) {
|
||||
getExecution().updateChunk(script, chunk, c, (delay, task) -> run(semaphore, task, delay));
|
||||
getExecution().updateChunk(script, chunk, c, (delay, task) -> run(semaphore, c, task, delay));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
semaphore.acquire(1024);
|
||||
} catch (InterruptedException ignored) {}
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
Iris.reportError(ex);
|
||||
}
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static Runnable run(Semaphore semaphore, Runnable runnable, int delay) {
|
||||
private static Runnable run(Semaphore semaphore, Chunk contextChunk, Runnable runnable, int delay) {
|
||||
return () -> {
|
||||
try {
|
||||
semaphore.acquire();
|
||||
@@ -382,13 +385,14 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
J.s(() -> {
|
||||
int effectiveDelay = J.isFolia() ? 0 : delay;
|
||||
J.runRegion(contextChunk.getWorld(), contextChunk.getX(), contextChunk.getZ(), () -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
}, delay);
|
||||
}, effectiveDelay);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,11 @@ package art.arcane.iris.engine.framework;
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.events.IrisEngineHotloadEvent;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.iris.util.format.C;
|
||||
import art.arcane.iris.util.math.Position2;
|
||||
import art.arcane.iris.util.plugin.VolmitSender;
|
||||
import org.bukkit.*;
|
||||
import art.arcane.iris.util.format.C;
|
||||
import art.arcane.iris.util.math.Position2;
|
||||
import art.arcane.iris.util.plugin.VolmitSender;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.EnderSignal;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -50,11 +51,11 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
|
||||
taskId = -1;
|
||||
}
|
||||
|
||||
public EngineAssignedWorldManager(Engine engine) {
|
||||
super(engine, "World");
|
||||
Iris.instance.registerListener(this);
|
||||
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::onTick, 0, 0);
|
||||
}
|
||||
public EngineAssignedWorldManager(Engine engine) {
|
||||
super(engine, "World");
|
||||
Iris.instance.registerListener(this);
|
||||
taskId = J.sr(this::onTick, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(IrisEngineHotloadEvent e) {
|
||||
@@ -134,9 +135,11 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
Iris.instance.unregisterListener(this);
|
||||
Bukkit.getScheduler().cancelTask(taskId);
|
||||
}
|
||||
}
|
||||
public void close() {
|
||||
super.close();
|
||||
Iris.instance.unregisterListener(this);
|
||||
if (taskId != -1) {
|
||||
J.csr(taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import art.arcane.iris.engine.object.annotations.ArrayType;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.Required;
|
||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -73,11 +74,13 @@ public class IrisCommand {
|
||||
.replaceAll("\\Q{y}\\E", String.valueOf(at.getBlockY()))
|
||||
.replaceAll("\\Q{z}\\E", String.valueOf(at.getBlockZ()));
|
||||
final String finalCommand = command;
|
||||
int safeDelay = (int) Math.max(0, Math.min(Integer.MAX_VALUE, delay));
|
||||
if (repeat) {
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), delay, repeatDelay);
|
||||
int safeRepeatDelay = (int) Math.max(1, Math.min(Integer.MAX_VALUE, repeatDelay));
|
||||
J.s(() -> J.sr(() -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), safeRepeatDelay), safeDelay);
|
||||
} else {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), delay);
|
||||
J.s(() -> Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalCommand), safeDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,9 +338,7 @@ public class IrisEntity extends IrisRegistrant {
|
||||
if (e instanceof Villager) {
|
||||
Villager villager = (Villager) e;
|
||||
villager.setRemoveWhenFarAway(false);
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
||||
villager.setPersistent(true);
|
||||
}, 1);
|
||||
J.s(() -> villager.setPersistent(true), 1);
|
||||
}
|
||||
|
||||
if (e instanceof Mob) {
|
||||
|
||||
@@ -80,7 +80,9 @@ public class LegacyTileData extends TileData {
|
||||
|
||||
@Override
|
||||
public void toBukkit(Block block) {
|
||||
J.s(() -> handler.toBukkit(block));
|
||||
if (!J.runAt(block.getLocation(), () -> handler.toBukkit(block))) {
|
||||
J.s(() -> handler.toBukkit(block));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,8 +23,8 @@ import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.iris.util.format.C;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import art.arcane.volmlib.util.reflect.V;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@@ -171,7 +171,7 @@ public class VirtualCommand {
|
||||
for (String i : command.getRequiredPermissions()) {
|
||||
if (!sender.hasPermission(i)) {
|
||||
failed = true;
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> sender.sendMessage("- " + C.WHITE + i), 0);
|
||||
J.s(() -> sender.sendMessage("- " + C.WHITE + i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
||||
@Override
|
||||
public void onDisable() {
|
||||
stop();
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
J.cancelPluginTasks();
|
||||
unregisterListener(this);
|
||||
unregisterAll();
|
||||
}
|
||||
@@ -339,6 +339,10 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command,
|
||||
String alias, String[] args) {
|
||||
if (commands == null || commands.isEmpty()) {
|
||||
return super.onTabComplete(sender, command, alias, args);
|
||||
}
|
||||
|
||||
KList<String> chain = new KList<>();
|
||||
|
||||
for (String i : args) {
|
||||
@@ -370,6 +374,9 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
||||
if (bad) {
|
||||
return false;
|
||||
}
|
||||
if (commands == null || commands.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KList<String> chain = new KList<>();
|
||||
chain.add(args);
|
||||
@@ -470,6 +477,9 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
||||
if (bad) {
|
||||
return;
|
||||
}
|
||||
if (commands == null || commands.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (VirtualCommand i : commands.v()) {
|
||||
try {
|
||||
unregisterCommand(i.getCommand());
|
||||
@@ -485,8 +495,15 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
||||
return;
|
||||
}
|
||||
for (org.bukkit.permissions.Permission i : computePermissions()) {
|
||||
Bukkit.getPluginManager().removePermission(i);
|
||||
v("Unregistered Permission " + i.getName());
|
||||
if (i == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Bukkit.getPluginManager().removePermission(i);
|
||||
v("Unregistered Permission " + i.getName());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,29 +20,39 @@ package art.arcane.iris.util.scheduling;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.service.PreservationSVC;
|
||||
import art.arcane.iris.util.parallel.MultiBurst;
|
||||
import art.arcane.volmlib.util.function.NastyFunction;
|
||||
import art.arcane.volmlib.util.function.NastyFuture;
|
||||
import art.arcane.volmlib.util.function.NastyRunnable;
|
||||
import art.arcane.volmlib.util.function.NastySupplier;
|
||||
import art.arcane.volmlib.util.math.FinalInteger;
|
||||
import art.arcane.volmlib.util.scheduling.AR;
|
||||
import art.arcane.volmlib.util.scheduling.FoliaScheduler;
|
||||
import art.arcane.volmlib.util.scheduling.JSupport;
|
||||
import art.arcane.volmlib.util.scheduling.SR;
|
||||
import art.arcane.volmlib.util.scheduling.SchedulerBridge;
|
||||
import art.arcane.volmlib.util.scheduling.StartupQueueSupport;
|
||||
import art.arcane.volmlib.util.math.FinalInteger;
|
||||
import art.arcane.iris.util.parallel.MultiBurst;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class J {
|
||||
private static int tid = 0;
|
||||
private static final long TICK_MS = 50L;
|
||||
private static final AtomicInteger TASK_IDS = new AtomicInteger(1);
|
||||
private static final Map<Integer, Runnable> REPEATING_CANCELLERS = new ConcurrentHashMap<>();
|
||||
private static final StartupQueueSupport STARTUP_QUEUE = new StartupQueueSupport();
|
||||
|
||||
static {
|
||||
@@ -100,10 +110,13 @@ public class J {
|
||||
}
|
||||
|
||||
public static void aBukkit(Runnable a) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
if (!isPluginEnabled()) {
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().scheduleAsyncDelayedTask(Iris.instance, a);
|
||||
|
||||
if (!runAsyncImmediate(a)) {
|
||||
a(a, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Future<T> a(Callable<T> a) {
|
||||
@@ -142,89 +155,210 @@ public class J {
|
||||
return JSupport.attempt(t::get, i, Iris::reportError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dont call this unless you know what you are doing!
|
||||
*/
|
||||
public static void executeAfterStartupQueue() {
|
||||
JSupport.executeAfterStartupQueue(STARTUP_QUEUE, J::s, J::a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a sync task to be run right after startup. If the server has already
|
||||
* started ticking, it will simply run it in a sync task.
|
||||
* <p>
|
||||
* If you dont know if you should queue this or not, do so, it's pretty
|
||||
* forgiving.
|
||||
*
|
||||
* @param r the runnable
|
||||
*/
|
||||
public static void ass(Runnable r) {
|
||||
JSupport.enqueueAfterStartupSync(STARTUP_QUEUE, r, J::s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule an async task to be run right after startup. If the server has
|
||||
* already started ticking, it will simply run it in an async task.
|
||||
* <p>
|
||||
* If you dont know if you should queue this or not, do so, it's pretty
|
||||
* forgiving.
|
||||
*
|
||||
* @param r the runnable
|
||||
*/
|
||||
public static void asa(Runnable r) {
|
||||
JSupport.enqueueAfterStartupAsync(STARTUP_QUEUE, r, J::a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a sync task
|
||||
*
|
||||
* @param r the runnable
|
||||
*/
|
||||
public static void s(Runnable r) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
public static boolean isFolia() {
|
||||
return FoliaScheduler.isFolia(Iris.instance);
|
||||
}
|
||||
|
||||
public static boolean isPrimaryThread() {
|
||||
return FoliaScheduler.isPrimaryThread();
|
||||
}
|
||||
|
||||
public static boolean isOwnedByCurrentRegion(Entity entity) {
|
||||
return FoliaScheduler.isOwnedByCurrentRegion(entity);
|
||||
}
|
||||
|
||||
public static boolean isOwnedByCurrentRegion(World world, int chunkX, int chunkZ) {
|
||||
return FoliaScheduler.isOwnedByCurrentRegion(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public static boolean runEntity(Entity entity, Runnable runnable) {
|
||||
if (entity == null || runnable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFolia()) {
|
||||
if (isOwnedByCurrentRegion(entity)) {
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
return runEntityImmediate(entity, runnable);
|
||||
}
|
||||
|
||||
if (isPrimaryThread()) {
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
s(runnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean runEntity(Entity entity, Runnable runnable, int delayTicks) {
|
||||
if (entity == null || runnable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delayTicks <= 0) {
|
||||
return runEntity(entity, runnable);
|
||||
}
|
||||
|
||||
if (isFolia() && runEntityDelayed(entity, runnable, delayTicks)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
s(() -> runEntity(entity, runnable), delayTicks);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean runRegion(World world, int chunkX, int chunkZ, Runnable runnable) {
|
||||
if (world == null || runnable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFolia() && isOwnedByCurrentRegion(world, chunkX, chunkZ)) {
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (runRegionImmediate(world, chunkX, chunkZ, runnable)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isFolia()) {
|
||||
Iris.verbose("Failed to schedule immediate region task for " + world.getName() + "@" + chunkX + "," + chunkZ + " on Folia.");
|
||||
return false;
|
||||
}
|
||||
|
||||
s(runnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean runRegion(World world, int chunkX, int chunkZ, Runnable runnable, int delayTicks) {
|
||||
if (world == null || runnable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delayTicks <= 0) {
|
||||
return runRegion(world, chunkX, chunkZ, runnable);
|
||||
}
|
||||
|
||||
if (runRegionDelayed(world, chunkX, chunkZ, runnable, delayTicks)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isFolia()) {
|
||||
Iris.verbose("Failed to schedule delayed region task for " + world.getName() + "@" + chunkX + "," + chunkZ
|
||||
+ " (" + delayTicks + "t) on Folia.");
|
||||
return false;
|
||||
}
|
||||
|
||||
s(runnable, delayTicks);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean runAt(Location location, Runnable runnable) {
|
||||
if (location == null || runnable == null || location.getWorld() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return runRegion(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, runnable);
|
||||
}
|
||||
|
||||
public static boolean runAt(Location location, Runnable runnable, int delayTicks) {
|
||||
if (location == null || runnable == null || location.getWorld() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return runRegion(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, runnable, delayTicks);
|
||||
}
|
||||
|
||||
public static void cancelPluginTasks() {
|
||||
if (Iris.instance == null) {
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r);
|
||||
|
||||
FoliaScheduler.cancelTasks(Iris.instance);
|
||||
|
||||
try {
|
||||
Bukkit.getScheduler().cancelTasks(Iris.instance);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// Folia blocks BukkitScheduler usage.
|
||||
Iris.verbose("Skipping BukkitScheduler#cancelTasks for Iris on this server.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void s(Runnable r) {
|
||||
if (!isPluginEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!runGlobalImmediate(r)) {
|
||||
try {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
throw new IllegalStateException("Failed to schedule sync task (Folia scheduler unavailable, BukkitScheduler unsupported).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CompletableFuture sfut(Runnable r) {
|
||||
CompletableFuture f = new CompletableFuture();
|
||||
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
if (!isPluginEnabled()) {
|
||||
return null;
|
||||
}
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
||||
|
||||
s(() -> {
|
||||
r.run();
|
||||
f.complete(null);
|
||||
});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<T> sfut(Supplier<T> r) {
|
||||
CompletableFuture<T> f = new CompletableFuture<>();
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
|
||||
if (!isPluginEnabled()) {
|
||||
return null;
|
||||
}
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
||||
|
||||
s(() -> {
|
||||
try {
|
||||
f.complete(r.get());
|
||||
} catch (Throwable e) {
|
||||
f.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public static CompletableFuture sfut(Runnable r, int delay) {
|
||||
CompletableFuture f = new CompletableFuture();
|
||||
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
if (!isPluginEnabled()) {
|
||||
return null;
|
||||
}
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
||||
|
||||
s(() -> {
|
||||
r.run();
|
||||
f.complete(null);
|
||||
}, delay);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -237,53 +371,58 @@ public class J {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a sync task
|
||||
*
|
||||
* @param r the runnable
|
||||
* @param delay the delay to wait in ticks before running
|
||||
*/
|
||||
public static void s(Runnable r, int delay) {
|
||||
if (!isPluginEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay <= 0) {
|
||||
s(r);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
return;
|
||||
if (!runGlobalDelayed(r, delay)) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r, delay);
|
||||
}
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r, delay);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a sync repeating task
|
||||
*
|
||||
* @param id the task id
|
||||
*/
|
||||
public static void csr(int id) {
|
||||
Bukkit.getScheduler().cancelTask(id);
|
||||
cancelRepeatingTask(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a sync repeating task
|
||||
*
|
||||
* @param r the runnable
|
||||
* @param interval the interval
|
||||
* @return the task id
|
||||
*/
|
||||
public static int sr(Runnable r, int interval) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
if (!isPluginEnabled()) {
|
||||
return -1;
|
||||
}
|
||||
return Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, r, 0, interval);
|
||||
|
||||
int safeInterval = Math.max(1, interval);
|
||||
RepeatingState state = new RepeatingState();
|
||||
int taskId = trackRepeatingTask(() -> state.cancelled = true);
|
||||
|
||||
Runnable[] loop = new Runnable[1];
|
||||
loop[0] = () -> {
|
||||
if (state.cancelled || !isPluginEnabled()) {
|
||||
REPEATING_CANCELLERS.remove(taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
r.run();
|
||||
if (state.cancelled || !isPluginEnabled()) {
|
||||
REPEATING_CANCELLERS.remove(taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
s(loop[0], safeInterval);
|
||||
};
|
||||
|
||||
s(loop[0]);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a sync repeating task for a limited amount of ticks
|
||||
*
|
||||
* @param r the runnable
|
||||
* @param interval the interval in ticks
|
||||
* @param intervals the maximum amount of intervals to run
|
||||
*/
|
||||
public static void sr(Runnable r, int interval, int intervals) {
|
||||
FinalInteger fi = new FinalInteger(0);
|
||||
|
||||
@@ -300,50 +439,60 @@ public class J {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Call an async task dealyed
|
||||
*
|
||||
* @param r the runnable
|
||||
* @param delay the delay to wait before running
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void a(Runnable r, int delay) {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
Bukkit.getScheduler().scheduleAsyncDelayedTask(Iris.instance, r, delay);
|
||||
if (!isPluginEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay <= 0) {
|
||||
if (!runAsyncImmediate(r)) {
|
||||
a(r);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!runAsyncDelayed(r, delay)) {
|
||||
a(() -> {
|
||||
if (sleep(ticksToMilliseconds(delay))) {
|
||||
r.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel an async repeat task
|
||||
*
|
||||
* @param id the id
|
||||
*/
|
||||
public static void car(int id) {
|
||||
Bukkit.getScheduler().cancelTask(id);
|
||||
cancelRepeatingTask(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an async repeat task
|
||||
*
|
||||
* @param r the runnable
|
||||
* @param interval the interval in ticks
|
||||
* @return the task id
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static int ar(Runnable r, int interval) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
||||
if (!isPluginEnabled()) {
|
||||
return -1;
|
||||
}
|
||||
return Bukkit.getScheduler().scheduleAsyncRepeatingTask(Iris.instance, r, 0, interval);
|
||||
|
||||
int safeInterval = Math.max(1, interval);
|
||||
RepeatingState state = new RepeatingState();
|
||||
int taskId = trackRepeatingTask(() -> state.cancelled = true);
|
||||
|
||||
Runnable[] loop = new Runnable[1];
|
||||
loop[0] = () -> {
|
||||
if (state.cancelled || !isPluginEnabled()) {
|
||||
REPEATING_CANCELLERS.remove(taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
r.run();
|
||||
if (state.cancelled || !isPluginEnabled()) {
|
||||
REPEATING_CANCELLERS.remove(taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
a(loop[0], safeInterval);
|
||||
};
|
||||
|
||||
a(loop[0], 0);
|
||||
return taskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an async repeating task for a limited time
|
||||
*
|
||||
* @param r the runnable
|
||||
* @param interval the interval
|
||||
* @param intervals the intervals to run
|
||||
*/
|
||||
public static void ar(Runnable r, int interval, int intervals) {
|
||||
FinalInteger fi = new FinalInteger(0);
|
||||
|
||||
@@ -359,4 +508,61 @@ public class J {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static int trackRepeatingTask(Runnable cancelAction) {
|
||||
int id = TASK_IDS.getAndIncrement();
|
||||
REPEATING_CANCELLERS.put(id, cancelAction);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void cancelRepeatingTask(int id) {
|
||||
Runnable cancelAction = REPEATING_CANCELLERS.remove(id);
|
||||
if (cancelAction != null) {
|
||||
cancelAction.run();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPluginEnabled() {
|
||||
return Iris.instance != null && Bukkit.getPluginManager().isPluginEnabled(Iris.instance);
|
||||
}
|
||||
|
||||
private static long ticksToMilliseconds(int ticks) {
|
||||
return Math.max(0L, ticks) * TICK_MS;
|
||||
}
|
||||
|
||||
private static boolean runGlobalImmediate(Runnable runnable) {
|
||||
return FoliaScheduler.runGlobal(Iris.instance, runnable);
|
||||
}
|
||||
|
||||
private static boolean runGlobalDelayed(Runnable runnable, int delayTicks) {
|
||||
return FoliaScheduler.runGlobal(Iris.instance, runnable, Math.max(0, delayTicks));
|
||||
}
|
||||
|
||||
private static boolean runRegionImmediate(World world, int chunkX, int chunkZ, Runnable runnable) {
|
||||
return FoliaScheduler.runRegion(Iris.instance, world, chunkX, chunkZ, runnable);
|
||||
}
|
||||
|
||||
private static boolean runRegionDelayed(World world, int chunkX, int chunkZ, Runnable runnable, int delayTicks) {
|
||||
return FoliaScheduler.runRegion(Iris.instance, world, chunkX, chunkZ, runnable, Math.max(0, delayTicks));
|
||||
}
|
||||
|
||||
private static boolean runAsyncImmediate(Runnable runnable) {
|
||||
return FoliaScheduler.runAsync(Iris.instance, runnable);
|
||||
}
|
||||
|
||||
private static boolean runAsyncDelayed(Runnable runnable, int delayTicks) {
|
||||
return FoliaScheduler.runAsync(Iris.instance, runnable, Math.max(0, delayTicks));
|
||||
}
|
||||
|
||||
private static boolean runEntityImmediate(Entity entity, Runnable runnable) {
|
||||
return FoliaScheduler.runEntity(Iris.instance, entity, runnable);
|
||||
}
|
||||
|
||||
private static boolean runEntityDelayed(Entity entity, Runnable runnable, int delayTicks) {
|
||||
return FoliaScheduler.runEntity(Iris.instance, entity, runnable, Math.max(0, delayTicks));
|
||||
}
|
||||
|
||||
private static final class RepeatingState {
|
||||
private volatile boolean cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.bytebuddy.agent.ByteBuddyAgent;
|
||||
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
@@ -30,17 +31,57 @@ public class Agent {
|
||||
public static boolean install() {
|
||||
if (isInstalled())
|
||||
return true;
|
||||
|
||||
if (!ensureAgentJar())
|
||||
return false;
|
||||
|
||||
try {
|
||||
Files.copy(Iris.instance.getResource("agent.jar"), AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
Iris.info("Installing Java Agent...");
|
||||
Iris.info("Note: JVM [Attach Listener/ERROR] [STDERR] warning lines during this step are expected and not Iris errors.");
|
||||
ByteBuddyAgent.attach(AGENT_JAR, ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Iris.error("Failed to install Java Agent: " + e.getMessage());
|
||||
Iris.reportError(e);
|
||||
}
|
||||
return doGetInstrumentation() != null;
|
||||
}
|
||||
|
||||
private static boolean ensureAgentJar() {
|
||||
File parent = AGENT_JAR.getParentFile();
|
||||
if (parent != null && !parent.exists() && !parent.mkdirs() && !parent.exists()) {
|
||||
Iris.error("Failed to create Iris plugin data folder for Java agent: " + parent.getAbsolutePath());
|
||||
return false;
|
||||
}
|
||||
|
||||
try (InputStream in = openBundledAgentJar()) {
|
||||
if (in == null) {
|
||||
if (AGENT_JAR.isFile() && AGENT_JAR.length() > 0) {
|
||||
Iris.warn("Bundled agent.jar not found in Iris plugin jar. Reusing existing " + AGENT_JAR.getAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
|
||||
Iris.error("Bundled agent.jar was not found in Iris plugin jar. Rebuild/deploy Iris with embedded agent.jar.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Files.copy(in, AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to prepare Java agent jar: " + e.getMessage());
|
||||
Iris.reportError(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static InputStream openBundledAgentJar() {
|
||||
InputStream stream = Iris.instance.getResource("agent.jar");
|
||||
if (stream != null) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
return Agent.class.getClassLoader().getResourceAsStream("agent.jar");
|
||||
}
|
||||
|
||||
private static Instrumentation doGetInstrumentation() {
|
||||
try {
|
||||
return (Instrumentation) Class.forName(NAME, true, ClassLoader.getSystemClassLoader()).getMethod("getInstrumentation").invoke(null);
|
||||
|
||||
@@ -3,7 +3,6 @@ package art.arcane.iris.util.profile;
|
||||
import art.arcane.volmlib.util.math.M;
|
||||
import art.arcane.iris.util.scheduling.J;
|
||||
import art.arcane.volmlib.util.scheduling.Looper;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
@@ -52,12 +51,13 @@ public abstract class MsptTimings extends Looper {
|
||||
protected abstract void update(int mspt);
|
||||
|
||||
private boolean startTickTask() {
|
||||
if (taskId != -1 && (Bukkit.getScheduler().isQueued(taskId) || Bukkit.getScheduler().isCurrentlyRunning(taskId)))
|
||||
if (taskId != -1)
|
||||
return false;
|
||||
|
||||
taskId = J.sr(() -> {
|
||||
if (isInterrupted()) {
|
||||
J.csr(taskId);
|
||||
taskId = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package art.arcane.iris.core.safeguard
|
||||
|
||||
import art.arcane.iris.Iris
|
||||
import art.arcane.iris.core.IrisSettings
|
||||
import art.arcane.iris.core.safeguard.task.Diagnostic
|
||||
import art.arcane.iris.core.safeguard.task.Task
|
||||
import art.arcane.iris.core.safeguard.task.ValueWithDiagnostics
|
||||
import art.arcane.iris.core.safeguard.task.tasks
|
||||
import art.arcane.iris.util.format.C
|
||||
import art.arcane.iris.util.scheduling.J
|
||||
import org.bukkit.Bukkit
|
||||
import java.util.*
|
||||
|
||||
object IrisSafeguard {
|
||||
@@ -93,51 +91,18 @@ object IrisSafeguard {
|
||||
|
||||
private fun warning() {
|
||||
Iris.warn(C.GOLD.toString() + "Iris is running in Warning Mode")
|
||||
|
||||
Iris.warn("")
|
||||
Iris.warn(C.DARK_GRAY.toString() + "--==<" + C.GOLD + " IMPORTANT " + C.DARK_GRAY + ">==--")
|
||||
Iris.warn(C.GOLD.toString() + "Iris is running in warning mode which may cause the following issues:")
|
||||
Iris.warn("- Data Loss")
|
||||
Iris.warn("- Errors")
|
||||
Iris.warn("- Broken worlds")
|
||||
Iris.warn("- Unexpected behavior.")
|
||||
Iris.warn("- And perhaps further complications.")
|
||||
Iris.warn(C.GRAY.toString() + "Some startup checks need attention. Review the messages above for tuning suggestions.")
|
||||
Iris.warn(C.GRAY.toString() + "Iris will continue startup normally.")
|
||||
Iris.warn("")
|
||||
}
|
||||
|
||||
private fun unstable() {
|
||||
Iris.error(C.DARK_RED.toString() + "Iris is running in Unstable Mode")
|
||||
|
||||
Iris.error(C.DARK_RED.toString() + "Iris is running in Danger Mode")
|
||||
Iris.error("")
|
||||
Iris.error(C.DARK_GRAY.toString() + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--")
|
||||
Iris.error("Iris is running in unstable mode which may cause the following issues:")
|
||||
Iris.error(C.DARK_RED.toString() + "Server Issues")
|
||||
Iris.error("- Server won't boot")
|
||||
Iris.error("- Data Loss")
|
||||
Iris.error("- Unexpected behavior.")
|
||||
Iris.error("- And More...")
|
||||
Iris.error(C.DARK_RED.toString() + "World Issues")
|
||||
Iris.error("- Worlds can't load due to corruption.")
|
||||
Iris.error("- Worlds may slowly corrupt until they can't load.")
|
||||
Iris.error("- World data loss.")
|
||||
Iris.error("- And More...")
|
||||
Iris.error(C.DARK_RED.toString() + "ATTENTION: " + C.RED + "While running Iris in unstable mode, you won't be eligible for support.")
|
||||
|
||||
if (IrisSettings.get().general.isDoomsdayAnnihilationSelfDestructMode) {
|
||||
Iris.error(C.DARK_RED.toString() + "Boot Unstable is set to true, continuing with the startup process in 10 seconds.")
|
||||
J.sleep(10000L)
|
||||
} else {
|
||||
Iris.error(C.DARK_RED.toString() + "Go to plugins/iris/settings.json and set DoomsdayAnnihilationSelfDestructMode to true if you wish to proceed.")
|
||||
Iris.error(C.DARK_RED.toString() + "The server will shutdown in 10 seconds.")
|
||||
J.sleep(10000L)
|
||||
Iris.error(C.DARK_RED.toString() + "Shutting down server.")
|
||||
forceShutdown = true
|
||||
try {
|
||||
Bukkit.getPluginManager().disablePlugins()
|
||||
} finally {
|
||||
Runtime.getRuntime().halt(42)
|
||||
}
|
||||
}
|
||||
Iris.error("Critical startup checks failed. Iris will continue startup in 10 seconds.")
|
||||
Iris.error("Review and resolve the errors above as soon as possible.")
|
||||
J.sleep(10000L)
|
||||
Iris.info("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,18 +13,25 @@ import art.arcane.iris.util.misc.getHardware
|
||||
import org.bukkit.Bukkit
|
||||
import java.util.Locale
|
||||
import java.util.stream.Collectors
|
||||
import javax.tools.ToolProvider
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
private val memory by task {
|
||||
val mem = getHardware.getProcessMemory()
|
||||
if (mem >= 5999) STABLE.withDiagnostics()
|
||||
else STABLE.withDiagnostics(
|
||||
WARN.create("Low Memory"),
|
||||
WARN.create("- 6GB+ Ram is recommended"),
|
||||
WARN.create("- Process Memory: $mem MB")
|
||||
)
|
||||
when {
|
||||
mem >= 3072 -> STABLE.withDiagnostics()
|
||||
mem > 2048 -> STABLE.withDiagnostics(
|
||||
INFO.create("Memory Recommendation"),
|
||||
INFO.create("- 3GB+ process memory is recommended for Iris."),
|
||||
INFO.create("- Process Memory: $mem MB")
|
||||
)
|
||||
else -> WARNING.withDiagnostics(
|
||||
WARN.create("Low Memory"),
|
||||
WARN.create("- Iris is running with 2GB or less process memory."),
|
||||
WARN.create("- 3GB+ process memory is recommended for Iris."),
|
||||
WARN.create("- Process Memory: $mem MB")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val incompatibilities by task {
|
||||
@@ -49,6 +56,7 @@ private val incompatibilities by task {
|
||||
|
||||
private val software by task {
|
||||
val supported = setOf(
|
||||
"folia",
|
||||
"purpur",
|
||||
"pufferfish",
|
||||
"paper",
|
||||
@@ -59,7 +67,7 @@ private val software by task {
|
||||
if (supported.any { server.name.contains(it, true) }) STABLE.withDiagnostics()
|
||||
else WARNING.withDiagnostics(
|
||||
WARN.create("Unsupported Server Software"),
|
||||
WARN.create("- Please consider using Paper or Purpur instead.")
|
||||
WARN.create("- Please consider using Folia, Paper, or Purpur instead.")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -118,12 +126,17 @@ private val diskSpace by task {
|
||||
|
||||
private val java by task {
|
||||
val version = Iris.getJavaVersion()
|
||||
val jdk = runCatching { ToolProvider.getSystemJavaCompiler() }.getOrNull() != null
|
||||
if (version in setOf(21) && jdk) STABLE.withDiagnostics()
|
||||
else WARNING.withDiagnostics(
|
||||
WARN.create("Unsupported Java version"),
|
||||
WARN.create("- Please consider using JDK 21 Instead of ${if(jdk) "JDK" else "JRE"} $version")
|
||||
)
|
||||
when {
|
||||
version == 21 -> STABLE.withDiagnostics()
|
||||
version > 21 -> STABLE.withDiagnostics(
|
||||
INFO.create("Java Runtime"),
|
||||
INFO.create("- Running Java $version. Iris is tested primarily on Java 21.")
|
||||
)
|
||||
else -> WARNING.withDiagnostics(
|
||||
WARN.create("Unsupported Java version"),
|
||||
WARN.create("- Java 21+ is recommended. Current runtime: Java $version")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +154,7 @@ val tasks = listOf(
|
||||
private val server get() = Bukkit.getServer()
|
||||
private fun isPaperPreferredServer(): Boolean {
|
||||
val name = server.name.lowercase(Locale.ROOT)
|
||||
return name.contains("paper") || name.contains("purpur") || name.contains("pufferfish")
|
||||
return name.contains("folia") || name.contains("paper") || name.contains("purpur") || name.contains("pufferfish")
|
||||
}
|
||||
private fun <T> MutableList<T>.addAll(vararg values: T) = values.forEach(this::add)
|
||||
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
name: ${name}
|
||||
version: ${version}
|
||||
main: ${main}
|
||||
load: STARTUP
|
||||
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
||||
name: ${name}
|
||||
version: ${version}
|
||||
main: ${main}
|
||||
folia-supported: true
|
||||
load: STARTUP
|
||||
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
||||
website: volmit.com
|
||||
description: More than a Dimension!
|
||||
commands:
|
||||
iris:
|
||||
aliases: [ ir, irs ]
|
||||
api-version: '${apiVersion}'
|
||||
api-version: '${apiVersion}'
|
||||
|
||||
@@ -215,25 +215,46 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
||||
if (map == null || pos == null || pos.getWorld() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tag converted = convertToTag(map, 0, 64);
|
||||
if (!(converted instanceof net.minecraft.nbt.CompoundTag tag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
J.s(() -> merge(level, blockPos, tag));
|
||||
if (!J.runAt(pos, () -> merge(level, blockPos, tag))) {
|
||||
Iris.warn("[NMS] Failed to schedule tile deserialize at " + blockPos + " in world " + pos.getWorld().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
||||
var blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||
var state = level.getBlockState(blockPos);
|
||||
if (!state.hasBlockEntity())
|
||||
return;
|
||||
|
||||
blockEntity = ((EntityBlock) state.getBlock())
|
||||
.newBlockEntity(blockPos, state);
|
||||
if (level == null || blockPos == null || tag == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||
var state = level.getBlockState(blockPos);
|
||||
if (!state.hasBlockEntity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockEntity = ((EntityBlock) state.getBlock())
|
||||
.newBlockEntity(blockPos, state);
|
||||
}
|
||||
|
||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||
accessor.setData(accessor.getData().merge(tag));
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("[NMS] Failed to merge tile data at " + blockPos + ": " + e.getMessage());
|
||||
Iris.reportError(e);
|
||||
}
|
||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||
accessor.setData(accessor.getData().merge(tag));
|
||||
}
|
||||
|
||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||
@@ -853,4 +874,4 @@ public class NMSBinding implements INMSBinding {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user