mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-06-18 14:50:57 +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 io.github.slimjar.resolver.data.Mirror
|
||||||
import org.ajoberstar.grgit.Grgit
|
import org.ajoberstar.grgit.Grgit
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
import org.gradle.jvm.tasks.Jar
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
@@ -157,6 +158,8 @@ slimJar {
|
|||||||
relocate("com.github.benmanes.caffeine", "$lib.caffeine")
|
relocate("com.github.benmanes.caffeine", "$lib.caffeine")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val embeddedAgentJar = project(":core:agent").tasks.named<Jar>("jar")
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
/**
|
/**
|
||||||
* We need parameter meta for the decree command system
|
* We need parameter meta for the decree command system
|
||||||
@@ -182,10 +185,14 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
dependsOn(embeddedAgentJar)
|
||||||
mergeServiceFiles()
|
mergeServiceFiles()
|
||||||
//minimize()
|
//minimize()
|
||||||
relocate("io.github.slimjar", "$lib.slimjar")
|
relocate("io.github.slimjar", "$lib.slimjar")
|
||||||
exclude("modules/loader-agent.isolated-jar")
|
exclude("modules/loader-agent.isolated-jar")
|
||||||
|
from(embeddedAgentJar.map { it.archiveFile }) {
|
||||||
|
rename { "agent.jar" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sentryCollectSourcesJava {
|
sentryCollectSourcesJava {
|
||||||
|
|||||||
@@ -98,8 +98,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
InstanceState.updateInstanceId();
|
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)) {
|
if (slicedClass == null || i.isAnnotationPresent(slicedClass)) {
|
||||||
try {
|
try {
|
||||||
v.add(i.getDeclaredConstructor().newInstance());
|
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)) {
|
if (slicedClass == null || i.isAnnotationPresent(slicedClass)) {
|
||||||
try {
|
try {
|
||||||
v.add(i);
|
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) {
|
} catch (Throwable e) {
|
||||||
try {
|
try {
|
||||||
instance.getLogger().info(instance.getTag() + string.replaceAll("(<([^>]+)>)", ""));
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
public static void later(NastyRunnable object) {
|
public static void later(NastyRunnable object) {
|
||||||
try {
|
try {
|
||||||
Bukkit.getScheduler().scheduleAsyncDelayedTask(instance, () ->
|
J.a(() -> {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
object.run();
|
object.run();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@@ -316,8 +326,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
}, RNG.r.i(100, 1200));
|
}, 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() {
|
public void addShutdownHook() {
|
||||||
if (shutdownHook != null) {
|
if (shutdownHook != null) {
|
||||||
|
try {
|
||||||
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||||
|
} catch (IllegalStateException ex) {
|
||||||
|
Iris.debug("Skipping shutdown hook replacement because JVM shutdown is already in progress.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
shutdownHook = new Thread(() -> {
|
shutdownHook = new Thread(() -> {
|
||||||
Bukkit.getWorlds()
|
Bukkit.getWorlds()
|
||||||
@@ -487,9 +504,15 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
|
|
||||||
MultiBurst.burst.close();
|
MultiBurst.burst.close();
|
||||||
MultiBurst.ioBurst.close();
|
MultiBurst.ioBurst.close();
|
||||||
|
if (services != null) {
|
||||||
services.clear();
|
services.clear();
|
||||||
});
|
}
|
||||||
|
}, "Iris-ShutdownHook");
|
||||||
|
try {
|
||||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
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) {
|
public void checkForBukkitWorlds(Predicate<String> filter) {
|
||||||
@@ -563,12 +586,14 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
|
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
if (IrisSafeguard.isForceShutdown()) return;
|
if (IrisSafeguard.isForceShutdown()) return;
|
||||||
|
if (services != null) {
|
||||||
services.values().forEach(IrisService::onDisable);
|
services.values().forEach(IrisService::onDisable);
|
||||||
|
}
|
||||||
if (configHotloadEngine != null) {
|
if (configHotloadEngine != null) {
|
||||||
configHotloadEngine.clear();
|
configHotloadEngine.clear();
|
||||||
configHotloadEngine = null;
|
configHotloadEngine = null;
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().cancelTasks(this);
|
J.cancelPluginTasks();
|
||||||
HandlerList.unregisterAll((Plugin) this);
|
HandlerList.unregisterAll((Plugin) this);
|
||||||
postShutdown.forEach(Runnable::run);
|
postShutdown.forEach(Runnable::run);
|
||||||
super.onDisable();
|
super.onDisable();
|
||||||
@@ -632,7 +657,12 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return IO.readAll(file);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -765,7 +795,10 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
JsonObject json = JsonParser.parseReader(r).getAsJsonObject();
|
JsonObject json = JsonParser.parseReader(r).getAsJsonObject();
|
||||||
if (json.has("version"))
|
if (json.has("version"))
|
||||||
version = json.get("version").getAsString();
|
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);
|
Iris.info(" " + dimName + " v" + version);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ public class IrisSettings {
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class IrisSettingsGeneral {
|
public static class IrisSettingsGeneral {
|
||||||
public boolean DoomsdayAnnihilationSelfDestructMode = false;
|
|
||||||
public boolean commandSounds = true;
|
public boolean commandSounds = true;
|
||||||
public boolean debug = false;
|
public boolean debug = false;
|
||||||
public boolean dumpMantleOnError = false;
|
public boolean dumpMantleOnError = false;
|
||||||
|
|||||||
@@ -104,7 +104,13 @@ public class ServerConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean installDataPacks(boolean fullInstall) {
|
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) {
|
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.misc.ServerProperties;
|
||||||
import art.arcane.iris.util.plugin.VolmitSender;
|
import art.arcane.iris.util.plugin.VolmitSender;
|
||||||
import art.arcane.iris.util.scheduling.J;
|
import art.arcane.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
@@ -120,6 +119,13 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
return;
|
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 {
|
try {
|
||||||
worldCreation = true;
|
worldCreation = true;
|
||||||
IrisToolbelt.createWorld()
|
IrisToolbelt.createWorld()
|
||||||
@@ -147,21 +153,99 @@ 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.");
|
if (main) sender().sendMessage(C.GREEN + "Your world will automatically be set as the main world when the server restarts.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
private boolean updateMainWorld(String newName) {
|
||||||
private void updateMainWorld(String newName) {
|
try {
|
||||||
File worlds = Bukkit.getWorldContainer();
|
File worlds = Bukkit.getWorldContainer();
|
||||||
var data = ServerProperties.DATA;
|
var data = ServerProperties.DATA;
|
||||||
try (var in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) {
|
try (var in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) {
|
||||||
data.load(in);
|
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")) {
|
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());
|
File source = new File(oldWorldFolder, sub);
|
||||||
|
if (!source.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IO.copyDirectory(source.toPath(), new File(newWorldFolder, sub).toPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
data.setProperty("level-name", newName);
|
data.setProperty("level-name", newName);
|
||||||
try (var out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) {
|
try (var out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) {
|
||||||
data.store(out, null);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decree(description = "Teleport to another world", aliases = {"tp"}, sync = true)
|
@Decree(description = "Teleport to another world", aliases = {"tp"}, sync = true)
|
||||||
@@ -439,22 +523,23 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
sender().sendMessage(C.GOLD + world + " is not an iris world.");
|
sender().sendMessage(C.GOLD + world + " is not an iris world.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sender().sendMessage(C.GREEN + "Loading world: " + world);
|
|
||||||
|
|
||||||
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
|
if (dimension == null) {
|
||||||
String gen = "Iris:" + dimension;
|
sender().sendMessage(C.RED + "Could not determine Iris dimension for " + world + ".");
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sender().sendMessage(C.GREEN + "Loading world: " + world);
|
||||||
|
|
||||||
|
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);
|
Iris.instance.checkForBukkitWorlds(world::equals);
|
||||||
sender().sendMessage(C.GREEN + world + " loaded successfully.");
|
sender().sendMessage(C.GREEN + world + " loaded successfully.");
|
||||||
}
|
}
|
||||||
@@ -498,7 +583,12 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
for (String key : data.getDimensionLoader().getPossibleKeys()) {
|
for (String key : data.getDimensionLoader().getPossibleKeys()) {
|
||||||
options.add(key);
|
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,
|
IrisDimension dimension,
|
||||||
@Param(defaultValue = "1337", description = "The seed to generate the studio with", aliases = "s")
|
@Param(defaultValue = "1337", description = "The seed to generate the studio with", aliases = "s")
|
||||||
long seed) {
|
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 + ")");
|
sender().sendMessage(C.GREEN + "Opening studio for the \"" + dimension.getName() + "\" pack (seed: " + seed + ")");
|
||||||
Iris.service(StudioSVC.class).open(sender(), seed, dimension.getLoadKey());
|
Iris.service(StudioSVC.class).open(sender(), seed, dimension.getLoadKey());
|
||||||
}
|
}
|
||||||
@@ -329,10 +336,10 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
var player = player();
|
var player = player();
|
||||||
var engine = engine();
|
var engine = engine();
|
||||||
|
|
||||||
ta.set(Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () ->
|
ta.set(J.sr(() ->
|
||||||
{
|
{
|
||||||
if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
|
if (!player.getOpenInventory().getType().equals(InventoryType.CHEST)) {
|
||||||
Bukkit.getScheduler().cancelTask(ta.get());
|
J.csr(ta.get());
|
||||||
sender.sendMessage(C.GREEN + "Opened inventory!");
|
sender.sendMessage(C.GREEN + "Opened inventory!");
|
||||||
return;
|
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);
|
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!");
|
sender().sendMessage(C.GREEN + "Opening inventory now!");
|
||||||
player().openInventory(inv);
|
player().openInventory(inv);
|
||||||
|
|||||||
@@ -93,7 +93,9 @@ public class INMS {
|
|||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
e.printStackTrace();
|
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.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.");
|
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;
|
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.INMS;
|
||||||
import art.arcane.iris.core.nms.datapack.v1192.DataFixerV1192;
|
import art.arcane.iris.core.nms.datapack.v1192.DataFixerV1192;
|
||||||
import art.arcane.iris.core.nms.datapack.v1206.DataFixerV1206;
|
import art.arcane.iris.core.nms.datapack.v1206.DataFixerV1206;
|
||||||
@@ -36,7 +37,21 @@ public enum DataVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static IDataFixer getDefault() {
|
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() {
|
public static DataVersion getLatest() {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import org.bukkit.World;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
@@ -235,16 +234,13 @@ public class DeepSearchPregenerator extends Thread implements Listener {
|
|||||||
}
|
}
|
||||||
save();
|
save();
|
||||||
jobs.remove(world.getName());
|
jobs.remove(world.getName());
|
||||||
new BukkitRunnable() {
|
J.a(() -> {
|
||||||
@Override
|
while (deepFile.exists()) {
|
||||||
public void run() {
|
|
||||||
while (deepFile.exists()){
|
|
||||||
deepFile.delete();
|
deepFile.delete();
|
||||||
J.sleep(1000);
|
J.sleep(1000);
|
||||||
}
|
}
|
||||||
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||||
}
|
}, 20);
|
||||||
}.runTaskLater(Iris.instance, 20L);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Iris.error("Failed to shutdown DeepSearch for " + world.getName());
|
Iris.error("Failed to shutdown DeepSearch for " + world.getName());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -271,4 +267,3 @@ public class DeepSearchPregenerator extends Thread implements Listener {
|
|||||||
boolean paused = false;
|
boolean paused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import org.bukkit.World;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -165,7 +164,10 @@ public class LazyPregenerator extends Thread implements Listener {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
latch.await();
|
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);
|
lazyGeneratedChunks.addAndGet(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -257,16 +259,13 @@ public class LazyPregenerator extends Thread implements Listener {
|
|||||||
}
|
}
|
||||||
save();
|
save();
|
||||||
jobs.remove(world.getName());
|
jobs.remove(world.getName());
|
||||||
new BukkitRunnable() {
|
J.a(() -> {
|
||||||
@Override
|
while (lazyFile.exists()) {
|
||||||
public void run() {
|
|
||||||
while (lazyFile.exists()){
|
|
||||||
lazyFile.delete();
|
lazyFile.delete();
|
||||||
J.sleep(1000);
|
J.sleep(1000);
|
||||||
}
|
}
|
||||||
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||||
}
|
}, 20);
|
||||||
}.runTaskLater(Iris.instance, 20L);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Iris.error("Failed to shutdown Lazygen for " + world.getName());
|
Iris.error("Failed to shutdown Lazygen for " + world.getName());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.bukkit.World;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
|
||||||
import org.checkerframework.checker.units.qual.N;
|
import org.checkerframework.checker.units.qual.N;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -233,7 +232,9 @@ public class TurboPregenerator extends Thread implements Listener {
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
latch.await();
|
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);
|
turboGeneratedChunks.addAndGet(1);
|
||||||
});
|
});
|
||||||
@@ -321,16 +322,13 @@ public class TurboPregenerator extends Thread implements Listener {
|
|||||||
}
|
}
|
||||||
save();
|
save();
|
||||||
jobs.remove(world.getName());
|
jobs.remove(world.getName());
|
||||||
new BukkitRunnable() {
|
J.a(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (turboFile.exists()) {
|
while (turboFile.exists()) {
|
||||||
turboFile.delete();
|
turboFile.delete();
|
||||||
J.sleep(1000);
|
J.sleep(1000);
|
||||||
}
|
}
|
||||||
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
|
||||||
}
|
}, 20);
|
||||||
}.runTaskLater(Iris.instance, 20L);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Iris.error("Failed to shutdown turbogen for " + world.getName());
|
Iris.error("Failed to shutdown turbogen for " + world.getName());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import art.arcane.iris.core.IrisSettings;
|
|||||||
import art.arcane.iris.core.loader.IrisData;
|
import art.arcane.iris.core.loader.IrisData;
|
||||||
import art.arcane.iris.core.tools.IrisToolbelt;
|
import art.arcane.iris.core.tools.IrisToolbelt;
|
||||||
import art.arcane.volmlib.util.board.Board;
|
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.BoardProvider;
|
||||||
import art.arcane.volmlib.util.board.BoardSettings;
|
import art.arcane.volmlib.util.board.BoardSettings;
|
||||||
import art.arcane.volmlib.util.board.ScoreDirection;
|
import art.arcane.volmlib.util.board.ScoreDirection;
|
||||||
@@ -43,40 +42,43 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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 {
|
public class BoardSVC implements IrisService, BoardProvider {
|
||||||
private final KMap<Player, PlayerBoard> boards = new KMap<>();
|
private final KMap<Player, PlayerBoard> boards = new KMap<>();
|
||||||
private ScheduledExecutorService executor;
|
private BoardSettings settings;
|
||||||
private BoardManager<Board> manager;
|
private boolean boardEnabled;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
executor = Executors.newScheduledThreadPool(0, Thread.ofVirtual().factory());
|
boardEnabled = true;
|
||||||
manager = new BoardManager<>(Iris.instance, BoardSettings.builder()
|
settings = BoardSettings.builder()
|
||||||
.boardProvider(this)
|
.boardProvider(this)
|
||||||
.scoreDirection(ScoreDirection.DOWN)
|
.scoreDirection(ScoreDirection.DOWN)
|
||||||
.build(), Board::new);
|
.build();
|
||||||
|
|
||||||
|
for (Player player : Iris.instance.getServer().getOnlinePlayers()) {
|
||||||
|
J.runEntity(player, () -> updatePlayer(player));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
executor.shutdownNow();
|
boardEnabled = false;
|
||||||
manager.onDisable();
|
for (PlayerBoard board : new ArrayList<>(boards.values())) {
|
||||||
|
board.cancel();
|
||||||
|
}
|
||||||
boards.clear();
|
boards.clear();
|
||||||
|
settings = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void on(PlayerChangedWorldEvent e) {
|
public void on(PlayerChangedWorldEvent e) {
|
||||||
J.s(() -> updatePlayer(e.getPlayer()));
|
J.runEntity(e.getPlayer(), () -> updatePlayer(e.getPlayer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void on(PlayerJoinEvent e) {
|
public void on(PlayerJoinEvent e) {
|
||||||
J.s(() -> updatePlayer(e.getPlayer()));
|
J.runEntity(e.getPlayer(), () -> updatePlayer(e.getPlayer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -85,16 +87,34 @@ public class BoardSVC implements IrisService, BoardProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updatePlayer(Player p) {
|
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())) {
|
if (IrisToolbelt.isIrisStudioWorld(p.getWorld())) {
|
||||||
manager.remove(p);
|
boards.computeIfAbsent(p, PlayerBoard::new);
|
||||||
manager.setup(p);
|
|
||||||
} else remove(p);
|
} else remove(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void remove(Player player) {
|
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);
|
var board = boards.remove(player);
|
||||||
if (board != null) board.task.cancel(true);
|
if (board != null) {
|
||||||
|
board.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,27 +124,60 @@ public class BoardSVC implements IrisService, BoardProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getLines(Player player) {
|
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
|
@Data
|
||||||
public class PlayerBoard {
|
public class PlayerBoard {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final ScheduledFuture<?> task;
|
private final Board board;
|
||||||
private volatile List<String> lines;
|
private volatile List<String> lines;
|
||||||
|
private volatile boolean cancelled;
|
||||||
|
|
||||||
public PlayerBoard(Player player) {
|
public PlayerBoard(Player player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
this.board = new Board(player, settings);
|
||||||
this.lines = new ArrayList<>();
|
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() {
|
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()) {
|
if (!Iris.service(StudioSVC.class).isProjectOpen()) {
|
||||||
|
board.update();
|
||||||
|
schedule(20);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
board.update();
|
||||||
|
schedule(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
cancelled = true;
|
||||||
|
J.runEntity(player, board::remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
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.collection.KMap;
|
||||||
import art.arcane.volmlib.util.math.M;
|
import art.arcane.volmlib.util.math.M;
|
||||||
import art.arcane.iris.util.plugin.IrisService;
|
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.World;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -33,16 +33,21 @@ import org.bukkit.event.world.WorldUnloadEvent;
|
|||||||
|
|
||||||
public class EditSVC implements IrisService {
|
public class EditSVC implements IrisService {
|
||||||
private KMap<World, BlockEditor> editors;
|
private KMap<World, BlockEditor> editors;
|
||||||
|
private int updateTaskId = -1;
|
||||||
public static boolean deletingWorld = false;
|
public static boolean deletingWorld = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
this.editors = new KMap<>();
|
this.editors = new KMap<>();
|
||||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::update, 1000, 1000);
|
updateTaskId = J.sr(this::update, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
|
if (updateTaskId != -1) {
|
||||||
|
J.csr(updateTaskId);
|
||||||
|
updateTaskId = -1;
|
||||||
|
}
|
||||||
flushNow();
|
flushNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.bukkit.event.world.WorldLoadEvent;
|
|||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
@@ -231,6 +232,20 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
updateTicker.start();
|
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 class Registered {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final PlatformChunkGenerator access;
|
private final PlatformChunkGenerator access;
|
||||||
@@ -261,12 +276,19 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) {
|
if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) {
|
||||||
trimmer = service.scheduleAtFixedRate(() -> {
|
trimmer = service.scheduleAtFixedRate(() -> {
|
||||||
Engine engine = getEngine();
|
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;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
engine.getMantle().trim(tectonicLimit());
|
engine.getMantle().trim(tectonicLimit());
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
if (isMantleClosed(e)) {
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
Iris.error("EngineSVC: Failed to trim for " + name);
|
Iris.error("EngineSVC: Failed to trim for " + name);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -277,7 +299,10 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
||||||
unloader = service.scheduleAtFixedRate(() -> {
|
unloader = service.scheduleAtFixedRate(() -> {
|
||||||
Engine engine = getEngine();
|
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;
|
return;
|
||||||
|
|
||||||
try {
|
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));
|
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
if (isMantleClosed(e)) {
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
Iris.error("EngineSVC: Failed to unload for " + name);
|
Iris.error("EngineSVC: Failed to unload for " + name);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import art.arcane.iris.Iris;
|
|||||||
import art.arcane.iris.util.plugin.IrisService;
|
import art.arcane.iris.util.plugin.IrisService;
|
||||||
import art.arcane.iris.util.scheduling.J;
|
import art.arcane.iris.util.scheduling.J;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
@@ -71,7 +70,7 @@ public class ObjectSVC implements IrisService {
|
|||||||
*/
|
*/
|
||||||
private void revert(Map<Block, BlockData> blocks) {
|
private void revert(Map<Block, BlockData> blocks) {
|
||||||
Iterator<Map.Entry<Block, BlockData>> it = blocks.entrySet().iterator();
|
Iterator<Map.Entry<Block, BlockData>> it = blocks.entrySet().iterator();
|
||||||
Bukkit.getScheduler().runTask(Iris.instance, () -> {
|
J.s(() -> {
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Map.Entry<Block, BlockData> entry = it.next();
|
Map.Entry<Block, BlockData> entry = it.next();
|
||||||
|
|||||||
@@ -341,8 +341,8 @@ public class StudioSVC implements IrisService {
|
|||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
sender.sendMessage("Error when creating studio world:");
|
sender.sendMessage("Failed to open studio world: " + e.getMessage());
|
||||||
e.printStackTrace();
|
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.");
|
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());
|
IrisDimension d = IrisToolbelt.getDimension(dimension());
|
||||||
|
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
@@ -171,6 +175,9 @@ public class IrisCreator {
|
|||||||
world = J.sfut(() -> INMS.get().createWorld(wc)).get();
|
world = J.sfut(() -> INMS.get().createWorld(wc)).get();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
done.set(true);
|
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);
|
throw new IrisException("Failed to create world!", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +233,22 @@ public class IrisCreator {
|
|||||||
return world;
|
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() {
|
private void addToBukkitYml() {
|
||||||
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
|
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
|
||||||
String gen = "Iris:" + dimension;
|
String gen = "Iris:" + dimension;
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -192,38 +191,85 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
|
|
||||||
private void discoverChunks() {
|
private void discoverChunks() {
|
||||||
var mantle = getEngine().getMantle().getMantle();
|
var mantle = getEngine().getMantle().getMantle();
|
||||||
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
|
World world = getEngine().getWorld().realWorld();
|
||||||
int r = 1;
|
if (world == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int x = -r; x <= r; x++) {
|
J.s(() -> {
|
||||||
for (int z = -r; z <= r; z++) {
|
for (Player player : world.getPlayers()) {
|
||||||
mantle.getChunk(i.getLocation().getChunk()).flag(MantleFlag.DISCOVERED, true);
|
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() {
|
private void updateChunks() {
|
||||||
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
|
World world = getEngine().getWorld().realWorld();
|
||||||
int r = 1;
|
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)) {
|
|
||||||
|
|
||||||
if (IrisSettings.get().getWorld().isPostLoadBlockUpdates()) {
|
|
||||||
getEngine().updateChunk(c.getWorld().getChunkAt(c.getX() + x, c.getZ() + z));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
|
J.s(() -> {
|
||||||
Chunk cx = getEngine().getWorld().realWorld().getChunkAt(c.getX() + x, c.getZ() + z);
|
for (Player player : world.getPlayers()) {
|
||||||
int finalX = c.getX() + x;
|
if (player == null || !player.isOnline()) {
|
||||||
int finalZ = c.getZ() + z;
|
continue;
|
||||||
J.a(() -> getMantle().raiseFlag(finalX, finalZ, MantleFlag.INITIAL_SPAWNED_MARKER,
|
}
|
||||||
() -> {
|
|
||||||
J.a(() -> spawnIn(cx, true), RNG.r.i(5, 200));
|
J.runEntity(player, () -> {
|
||||||
getSpawnersFromMarkers(cx).forEach((blockf, spawners) -> {
|
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++) {
|
||||||
|
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()) {
|
if (spawners.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -232,12 +278,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||||
spawn(block, s, true);
|
spawn(block, s, true);
|
||||||
});
|
});
|
||||||
}));
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onAsyncTick() {
|
private boolean onAsyncTick() {
|
||||||
@@ -274,8 +315,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int spawnBuffer = RNG.r.i(2, 12);
|
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) {
|
while (spawnBuffer-- > 0) {
|
||||||
if (cc.length == 0) {
|
if (cc.length == 0) {
|
||||||
Iris.debug("Can't spawn. No chunks!");
|
Iris.debug("Can't spawn. No chunks!");
|
||||||
@@ -283,18 +328,60 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Chunk c = cc[RNG.r.nextInt(cc.length)];
|
Chunk c = cc[RNG.r.nextInt(cc.length)];
|
||||||
|
spawnChunkSafely(world, c.getX(), c.getZ(), false);
|
||||||
if (!c.isLoaded() || !Chunks.isSafe(c.getWorld(), c.getX(), c.getZ())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
spawnIn(c, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
energy -= (actuallySpawned / 2D);
|
energy -= (actuallySpawned / 2D);
|
||||||
return actuallySpawned > 0;
|
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() {
|
private void fixEnergy() {
|
||||||
energy = M.clip(energy, 1D, getDimension().getMaximumEnergy());
|
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());
|
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
|
||||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||||
spawn(block, s, false);
|
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)));
|
() -> spawn(block, s, true)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -359,7 +446,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
try {
|
try {
|
||||||
spawn(c, v);
|
spawn(c, v);
|
||||||
} catch (Throwable e) {
|
} 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) {
|
public void execute(Future<Chunk> chunkFuture) {
|
||||||
try {
|
try {
|
||||||
chunkFuture.get();
|
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);
|
IrisPosition pos = new IrisPosition((c.getX() << 4) + x, y, (c.getZ() << 4) + z);
|
||||||
|
|
||||||
if (mark.isEmptyAbove()) {
|
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 {
|
if (remove) {
|
||||||
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()) {
|
|
||||||
b.add(pos);
|
b.add(pos);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,20 +297,20 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
try {
|
try {
|
||||||
Semaphore semaphore = new Semaphore(1024);
|
Semaphore semaphore = new Semaphore(1024);
|
||||||
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
|
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) -> {
|
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
|
||||||
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
|
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
|
||||||
if (!TileData.setTileState(block, v.getData()))
|
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());
|
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));
|
}, 0));
|
||||||
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, () -> {
|
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, c, () -> {
|
||||||
chunk.iterate(Identifier.class, (x, y, z, v) -> {
|
chunk.iterate(Identifier.class, (x, y, z, v) -> {
|
||||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||||
});
|
});
|
||||||
}, 0));
|
}, 0));
|
||||||
|
|
||||||
chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, () -> {
|
chunk.raiseFlagUnchecked(MantleFlag.UPDATE, run(semaphore, c, () -> {
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
int[][] grid = new int[16][16];
|
int[][] grid = new int[16][16];
|
||||||
for (int x = 0; x < 16; x++) {
|
for (int x = 0; x < 16; x++) {
|
||||||
@@ -362,19 +362,22 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (var script : scripts) {
|
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 {
|
try {
|
||||||
semaphore.acquire(1024);
|
semaphore.acquire(1024);
|
||||||
} catch (InterruptedException ignored) {}
|
} catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
Iris.reportError(ex);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
chunk.release();
|
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 () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
semaphore.acquire();
|
semaphore.acquire();
|
||||||
@@ -382,13 +385,14 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
J.s(() -> {
|
int effectiveDelay = J.isFolia() ? 0 : delay;
|
||||||
|
J.runRegion(contextChunk.getWorld(), contextChunk.getX(), contextChunk.getZ(), () -> {
|
||||||
try {
|
try {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
} finally {
|
} finally {
|
||||||
semaphore.release();
|
semaphore.release();
|
||||||
}
|
}
|
||||||
}, delay);
|
}, effectiveDelay);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import art.arcane.volmlib.util.collection.KList;
|
|||||||
import art.arcane.iris.util.format.C;
|
import art.arcane.iris.util.format.C;
|
||||||
import art.arcane.iris.util.math.Position2;
|
import art.arcane.iris.util.math.Position2;
|
||||||
import art.arcane.iris.util.plugin.VolmitSender;
|
import art.arcane.iris.util.plugin.VolmitSender;
|
||||||
|
import art.arcane.iris.util.scheduling.J;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.entity.EnderSignal;
|
import org.bukkit.entity.EnderSignal;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@@ -53,7 +54,7 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
|
|||||||
public EngineAssignedWorldManager(Engine engine) {
|
public EngineAssignedWorldManager(Engine engine) {
|
||||||
super(engine, "World");
|
super(engine, "World");
|
||||||
Iris.instance.registerListener(this);
|
Iris.instance.registerListener(this);
|
||||||
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::onTick, 0, 0);
|
taskId = J.sr(this::onTick, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -137,6 +138,8 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
|
|||||||
public void close() {
|
public void close() {
|
||||||
super.close();
|
super.close();
|
||||||
Iris.instance.unregisterListener(this);
|
Iris.instance.unregisterListener(this);
|
||||||
Bukkit.getScheduler().cancelTask(taskId);
|
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.Desc;
|
||||||
import art.arcane.iris.engine.object.annotations.Required;
|
import art.arcane.iris.engine.object.annotations.Required;
|
||||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||||
|
import art.arcane.iris.util.scheduling.J;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -73,10 +74,12 @@ public class IrisCommand {
|
|||||||
.replaceAll("\\Q{y}\\E", String.valueOf(at.getBlockY()))
|
.replaceAll("\\Q{y}\\E", String.valueOf(at.getBlockY()))
|
||||||
.replaceAll("\\Q{z}\\E", String.valueOf(at.getBlockZ()));
|
.replaceAll("\\Q{z}\\E", String.valueOf(at.getBlockZ()));
|
||||||
final String finalCommand = command;
|
final String finalCommand = command;
|
||||||
|
int safeDelay = (int) Math.max(0, Math.min(Integer.MAX_VALUE, delay));
|
||||||
if (repeat) {
|
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 {
|
} 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) {
|
if (e instanceof Villager) {
|
||||||
Villager villager = (Villager) e;
|
Villager villager = (Villager) e;
|
||||||
villager.setRemoveWhenFarAway(false);
|
villager.setRemoveWhenFarAway(false);
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
J.s(() -> villager.setPersistent(true), 1);
|
||||||
villager.setPersistent(true);
|
|
||||||
}, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e instanceof Mob) {
|
if (e instanceof Mob) {
|
||||||
|
|||||||
@@ -80,8 +80,10 @@ public class LegacyTileData extends TileData {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toBukkit(Block block) {
|
public void toBukkit(Block block) {
|
||||||
|
if (!J.runAt(block.getLocation(), () -> handler.toBukkit(block))) {
|
||||||
J.s(() -> handler.toBukkit(block));
|
J.s(() -> handler.toBukkit(block));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toBinary(DataOutputStream out) throws IOException {
|
public void toBinary(DataOutputStream out) throws IOException {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import art.arcane.iris.core.IrisSettings;
|
|||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.collection.KMap;
|
import art.arcane.volmlib.util.collection.KMap;
|
||||||
import art.arcane.iris.util.format.C;
|
import art.arcane.iris.util.format.C;
|
||||||
|
import art.arcane.iris.util.scheduling.J;
|
||||||
import art.arcane.volmlib.util.reflect.V;
|
import art.arcane.volmlib.util.reflect.V;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ public class VirtualCommand {
|
|||||||
for (String i : command.getRequiredPermissions()) {
|
for (String i : command.getRequiredPermissions()) {
|
||||||
if (!sender.hasPermission(i)) {
|
if (!sender.hasPermission(i)) {
|
||||||
failed = true;
|
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
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
stop();
|
stop();
|
||||||
Bukkit.getScheduler().cancelTasks(this);
|
J.cancelPluginTasks();
|
||||||
unregisterListener(this);
|
unregisterListener(this);
|
||||||
unregisterAll();
|
unregisterAll();
|
||||||
}
|
}
|
||||||
@@ -339,6 +339,10 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
|||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(CommandSender sender, Command command,
|
public List<String> onTabComplete(CommandSender sender, Command command,
|
||||||
String alias, String[] args) {
|
String alias, String[] args) {
|
||||||
|
if (commands == null || commands.isEmpty()) {
|
||||||
|
return super.onTabComplete(sender, command, alias, args);
|
||||||
|
}
|
||||||
|
|
||||||
KList<String> chain = new KList<>();
|
KList<String> chain = new KList<>();
|
||||||
|
|
||||||
for (String i : args) {
|
for (String i : args) {
|
||||||
@@ -370,6 +374,9 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
|||||||
if (bad) {
|
if (bad) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (commands == null || commands.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
KList<String> chain = new KList<>();
|
KList<String> chain = new KList<>();
|
||||||
chain.add(args);
|
chain.add(args);
|
||||||
@@ -470,6 +477,9 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
|||||||
if (bad) {
|
if (bad) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (commands == null || commands.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (VirtualCommand i : commands.v()) {
|
for (VirtualCommand i : commands.v()) {
|
||||||
try {
|
try {
|
||||||
unregisterCommand(i.getCommand());
|
unregisterCommand(i.getCommand());
|
||||||
@@ -485,8 +495,15 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (org.bukkit.permissions.Permission i : computePermissions()) {
|
for (org.bukkit.permissions.Permission i : computePermissions()) {
|
||||||
|
if (i == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
Bukkit.getPluginManager().removePermission(i);
|
Bukkit.getPluginManager().removePermission(i);
|
||||||
v("Unregistered Permission " + i.getName());
|
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.Iris;
|
||||||
import art.arcane.iris.core.service.PreservationSVC;
|
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.NastyFunction;
|
||||||
import art.arcane.volmlib.util.function.NastyFuture;
|
import art.arcane.volmlib.util.function.NastyFuture;
|
||||||
import art.arcane.volmlib.util.function.NastyRunnable;
|
import art.arcane.volmlib.util.function.NastyRunnable;
|
||||||
import art.arcane.volmlib.util.function.NastySupplier;
|
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.AR;
|
||||||
|
import art.arcane.volmlib.util.scheduling.FoliaScheduler;
|
||||||
import art.arcane.volmlib.util.scheduling.JSupport;
|
import art.arcane.volmlib.util.scheduling.JSupport;
|
||||||
import art.arcane.volmlib.util.scheduling.SR;
|
import art.arcane.volmlib.util.scheduling.SR;
|
||||||
import art.arcane.volmlib.util.scheduling.SchedulerBridge;
|
import art.arcane.volmlib.util.scheduling.SchedulerBridge;
|
||||||
import art.arcane.volmlib.util.scheduling.StartupQueueSupport;
|
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.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.Callable;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
public class J {
|
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();
|
private static final StartupQueueSupport STARTUP_QUEUE = new StartupQueueSupport();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -100,10 +110,13 @@ public class J {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void aBukkit(Runnable a) {
|
public static void aBukkit(Runnable a) {
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
if (!isPluginEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().scheduleAsyncDelayedTask(Iris.instance, a);
|
|
||||||
|
if (!runAsyncImmediate(a)) {
|
||||||
|
a(a, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Future<T> a(Callable<T> a) {
|
public static <T> Future<T> a(Callable<T> a) {
|
||||||
@@ -142,89 +155,210 @@ public class J {
|
|||||||
return JSupport.attempt(t::get, i, Iris::reportError);
|
return JSupport.attempt(t::get, i, Iris::reportError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Dont call this unless you know what you are doing!
|
|
||||||
*/
|
|
||||||
public static void executeAfterStartupQueue() {
|
public static void executeAfterStartupQueue() {
|
||||||
JSupport.executeAfterStartupQueue(STARTUP_QUEUE, J::s, J::a);
|
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) {
|
public static void ass(Runnable r) {
|
||||||
JSupport.enqueueAfterStartupSync(STARTUP_QUEUE, r, J::s);
|
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) {
|
public static void asa(Runnable r) {
|
||||||
JSupport.enqueueAfterStartupAsync(STARTUP_QUEUE, r, J::a);
|
JSupport.enqueueAfterStartupAsync(STARTUP_QUEUE, r, J::a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static boolean isFolia() {
|
||||||
* Queue a sync task
|
return FoliaScheduler.isFolia(Iris.instance);
|
||||||
*
|
}
|
||||||
* @param r the runnable
|
|
||||||
*/
|
public static boolean isPrimaryThread() {
|
||||||
public static void s(Runnable r) {
|
return FoliaScheduler.isPrimaryThread();
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
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) {
|
public static CompletableFuture sfut(Runnable r) {
|
||||||
CompletableFuture f = new CompletableFuture();
|
CompletableFuture f = new CompletableFuture();
|
||||||
|
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
if (!isPluginEnabled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
|
||||||
|
s(() -> {
|
||||||
r.run();
|
r.run();
|
||||||
f.complete(null);
|
f.complete(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> CompletableFuture<T> sfut(Supplier<T> r) {
|
public static <T> CompletableFuture<T> sfut(Supplier<T> r) {
|
||||||
CompletableFuture<T> f = new CompletableFuture<>();
|
CompletableFuture<T> f = new CompletableFuture<>();
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
|
||||||
|
if (!isPluginEnabled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
|
||||||
|
s(() -> {
|
||||||
try {
|
try {
|
||||||
f.complete(r.get());
|
f.complete(r.get());
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
f.completeExceptionally(e);
|
f.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture sfut(Runnable r, int delay) {
|
public static CompletableFuture sfut(Runnable r, int delay) {
|
||||||
CompletableFuture f = new CompletableFuture();
|
CompletableFuture f = new CompletableFuture();
|
||||||
|
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
if (!isPluginEnabled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> {
|
|
||||||
|
s(() -> {
|
||||||
r.run();
|
r.run();
|
||||||
f.complete(null);
|
f.complete(null);
|
||||||
}, delay);
|
}, delay);
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,53 +371,58 @@ public class J {
|
|||||||
return f;
|
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) {
|
public static void s(Runnable r, int delay) {
|
||||||
try {
|
if (!isPluginEnabled()) {
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (delay <= 0) {
|
||||||
|
s(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!runGlobalDelayed(r, delay)) {
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r, delay);
|
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, r, delay);
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel a sync repeating task
|
|
||||||
*
|
|
||||||
* @param id the task id
|
|
||||||
*/
|
|
||||||
public static void csr(int 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) {
|
public static int sr(Runnable r, int interval) {
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
if (!isPluginEnabled()) {
|
||||||
return -1;
|
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) {
|
public static void sr(Runnable r, int interval, int intervals) {
|
||||||
FinalInteger fi = new FinalInteger(0);
|
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) {
|
public static void a(Runnable r, int delay) {
|
||||||
if (Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
if (!isPluginEnabled()) {
|
||||||
Bukkit.getScheduler().scheduleAsyncDelayedTask(Iris.instance, r, delay);
|
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) {
|
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) {
|
public static int ar(Runnable r, int interval) {
|
||||||
if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) {
|
if (!isPluginEnabled()) {
|
||||||
return -1;
|
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) {
|
public static void ar(Runnable r, int interval, int intervals) {
|
||||||
FinalInteger fi = new FinalInteger(0);
|
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 net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
@@ -30,17 +31,57 @@ public class Agent {
|
|||||||
public static boolean install() {
|
public static boolean install() {
|
||||||
if (isInstalled())
|
if (isInstalled())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!ensureAgentJar())
|
||||||
|
return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.copy(Iris.instance.getResource("agent.jar"), AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
Iris.info("Installing Java Agent...");
|
Iris.info("Installing Java Agent...");
|
||||||
Iris.info("Note: JVM [Attach Listener/ERROR] [STDERR] warning lines during this step are expected and not Iris errors.");
|
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);
|
ByteBuddyAgent.attach(AGENT_JAR, ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
Iris.error("Failed to install Java Agent: " + e.getMessage());
|
||||||
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
return doGetInstrumentation() != null;
|
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() {
|
private static Instrumentation doGetInstrumentation() {
|
||||||
try {
|
try {
|
||||||
return (Instrumentation) Class.forName(NAME, true, ClassLoader.getSystemClassLoader()).getMethod("getInstrumentation").invoke(null);
|
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.volmlib.util.math.M;
|
||||||
import art.arcane.iris.util.scheduling.J;
|
import art.arcane.iris.util.scheduling.J;
|
||||||
import art.arcane.volmlib.util.scheduling.Looper;
|
import art.arcane.volmlib.util.scheduling.Looper;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -52,12 +51,13 @@ public abstract class MsptTimings extends Looper {
|
|||||||
protected abstract void update(int mspt);
|
protected abstract void update(int mspt);
|
||||||
|
|
||||||
private boolean startTickTask() {
|
private boolean startTickTask() {
|
||||||
if (taskId != -1 && (Bukkit.getScheduler().isQueued(taskId) || Bukkit.getScheduler().isCurrentlyRunning(taskId)))
|
if (taskId != -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
taskId = J.sr(() -> {
|
taskId = J.sr(() -> {
|
||||||
if (isInterrupted()) {
|
if (isInterrupted()) {
|
||||||
J.csr(taskId);
|
J.csr(taskId);
|
||||||
|
taskId = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package art.arcane.iris.core.safeguard
|
package art.arcane.iris.core.safeguard
|
||||||
|
|
||||||
import art.arcane.iris.Iris
|
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.Diagnostic
|
||||||
import art.arcane.iris.core.safeguard.task.Task
|
import art.arcane.iris.core.safeguard.task.Task
|
||||||
import art.arcane.iris.core.safeguard.task.ValueWithDiagnostics
|
import art.arcane.iris.core.safeguard.task.ValueWithDiagnostics
|
||||||
import art.arcane.iris.core.safeguard.task.tasks
|
import art.arcane.iris.core.safeguard.task.tasks
|
||||||
import art.arcane.iris.util.format.C
|
import art.arcane.iris.util.format.C
|
||||||
import art.arcane.iris.util.scheduling.J
|
import art.arcane.iris.util.scheduling.J
|
||||||
import org.bukkit.Bukkit
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object IrisSafeguard {
|
object IrisSafeguard {
|
||||||
@@ -93,51 +91,18 @@ object IrisSafeguard {
|
|||||||
|
|
||||||
private fun warning() {
|
private fun warning() {
|
||||||
Iris.warn(C.GOLD.toString() + "Iris is running in Warning Mode")
|
Iris.warn(C.GOLD.toString() + "Iris is running in Warning Mode")
|
||||||
|
Iris.warn(C.GRAY.toString() + "Some startup checks need attention. Review the messages above for tuning suggestions.")
|
||||||
Iris.warn("")
|
Iris.warn(C.GRAY.toString() + "Iris will continue startup normally.")
|
||||||
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("")
|
Iris.warn("")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unstable() {
|
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("")
|
||||||
Iris.error(C.DARK_GRAY.toString() + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--")
|
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("Critical startup checks failed. Iris will continue startup in 10 seconds.")
|
||||||
Iris.error(C.DARK_RED.toString() + "Server Issues")
|
Iris.error("Review and resolve the errors above as soon as possible.")
|
||||||
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)
|
J.sleep(10000L)
|
||||||
} else {
|
|
||||||
Iris.error(C.DARK_RED.toString() + "Go to plugins/iris/settings.json and set DoomsdayAnnihilationSelfDestructMode to true if you wish to proceed.")
|
|
||||||
Iris.error(C.DARK_RED.toString() + "The server will shutdown in 10 seconds.")
|
|
||||||
J.sleep(10000L)
|
|
||||||
Iris.error(C.DARK_RED.toString() + "Shutting down server.")
|
|
||||||
forceShutdown = true
|
|
||||||
try {
|
|
||||||
Bukkit.getPluginManager().disablePlugins()
|
|
||||||
} finally {
|
|
||||||
Runtime.getRuntime().halt(42)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Iris.info("")
|
Iris.info("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,18 +13,25 @@ import art.arcane.iris.util.misc.getHardware
|
|||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.stream.Collectors
|
import java.util.stream.Collectors
|
||||||
import javax.tools.ToolProvider
|
|
||||||
import kotlin.properties.PropertyDelegateProvider
|
import kotlin.properties.PropertyDelegateProvider
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
|
||||||
private val memory by task {
|
private val memory by task {
|
||||||
val mem = getHardware.getProcessMemory()
|
val mem = getHardware.getProcessMemory()
|
||||||
if (mem >= 5999) STABLE.withDiagnostics()
|
when {
|
||||||
else STABLE.withDiagnostics(
|
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("Low Memory"),
|
||||||
WARN.create("- 6GB+ Ram is recommended"),
|
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")
|
WARN.create("- Process Memory: $mem MB")
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val incompatibilities by task {
|
private val incompatibilities by task {
|
||||||
@@ -49,6 +56,7 @@ private val incompatibilities by task {
|
|||||||
|
|
||||||
private val software by task {
|
private val software by task {
|
||||||
val supported = setOf(
|
val supported = setOf(
|
||||||
|
"folia",
|
||||||
"purpur",
|
"purpur",
|
||||||
"pufferfish",
|
"pufferfish",
|
||||||
"paper",
|
"paper",
|
||||||
@@ -59,7 +67,7 @@ private val software by task {
|
|||||||
if (supported.any { server.name.contains(it, true) }) STABLE.withDiagnostics()
|
if (supported.any { server.name.contains(it, true) }) STABLE.withDiagnostics()
|
||||||
else WARNING.withDiagnostics(
|
else WARNING.withDiagnostics(
|
||||||
WARN.create("Unsupported Server Software"),
|
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 {
|
private val java by task {
|
||||||
val version = Iris.getJavaVersion()
|
val version = Iris.getJavaVersion()
|
||||||
val jdk = runCatching { ToolProvider.getSystemJavaCompiler() }.getOrNull() != null
|
when {
|
||||||
if (version in setOf(21) && jdk) STABLE.withDiagnostics()
|
version == 21 -> STABLE.withDiagnostics()
|
||||||
else WARNING.withDiagnostics(
|
version > 21 -> STABLE.withDiagnostics(
|
||||||
WARN.create("Unsupported Java version"),
|
INFO.create("Java Runtime"),
|
||||||
WARN.create("- Please consider using JDK 21 Instead of ${if(jdk) "JDK" else "JRE"} $version")
|
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 val server get() = Bukkit.getServer()
|
||||||
private fun isPaperPreferredServer(): Boolean {
|
private fun isPaperPreferredServer(): Boolean {
|
||||||
val name = server.name.lowercase(Locale.ROOT)
|
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)
|
private fun <T> MutableList<T>.addAll(vararg values: T) = values.forEach(this::add)
|
||||||
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
fun task(action: () -> ValueWithDiagnostics<Mode>) = PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, Task>> { _, _ ->
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
name: ${name}
|
name: ${name}
|
||||||
version: ${version}
|
version: ${version}
|
||||||
main: ${main}
|
main: ${main}
|
||||||
|
folia-supported: true
|
||||||
load: STARTUP
|
load: STARTUP
|
||||||
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
authors: [ cyberpwn, NextdoorPsycho, Vatuu ]
|
||||||
website: volmit.com
|
website: volmit.com
|
||||||
|
|||||||
@@ -215,25 +215,46 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
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 level = ((CraftWorld) pos.getWorld()).getHandle();
|
||||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
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) {
|
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
||||||
|
if (level == null || blockPos == null || tag == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
var blockEntity = level.getBlockEntity(blockPos);
|
var blockEntity = level.getBlockEntity(blockPos);
|
||||||
if (blockEntity == null) {
|
if (blockEntity == null) {
|
||||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
||||||
var state = level.getBlockState(blockPos);
|
var state = level.getBlockState(blockPos);
|
||||||
if (!state.hasBlockEntity())
|
if (!state.hasBlockEntity()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
blockEntity = ((EntityBlock) state.getBlock())
|
blockEntity = ((EntityBlock) state.getBlock())
|
||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(accessor.getData().merge(tag));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.warn("[NMS] Failed to merge tile data at " + blockPos + ": " + e.getMessage());
|
||||||
|
Iris.reportError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
|||||||
Reference in New Issue
Block a user