mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
object cleanup
This commit is contained in:
@@ -96,7 +96,6 @@ import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.volmlib.util.exceptions.IrisException;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
import art.arcane.volmlib.util.function.NastyRunnable;
|
||||
import art.arcane.volmlib.util.hotload.ConfigHotloadEngine;
|
||||
import art.arcane.volmlib.util.hud.HudActionBar;
|
||||
import art.arcane.volmlib.util.hud.HudBossBarLane;
|
||||
import art.arcane.volmlib.util.io.IO;
|
||||
@@ -165,7 +164,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
public static Bindings.Adventure audiences;
|
||||
public static MultiverseCoreLink linkMultiverseCore;
|
||||
public static IrisCompat compat;
|
||||
public static ConfigHotloadEngine configHotloadEngine;
|
||||
public static ChunkTickets tickets;
|
||||
private static VolmitSender sender;
|
||||
private static Thread shutdownHook;
|
||||
@@ -663,13 +661,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
IrisServices.register(ManagedWorldLoader.class, (ManagedWorldLoader) this::loadManagedWorld);
|
||||
SettingsHotloadWatch watch = new SettingsHotloadWatch(getDataFile("settings.json"));
|
||||
settingsHotloadWatch = watch;
|
||||
configHotloadEngine = new ConfigHotloadEngine(
|
||||
watch::isSettingsFile,
|
||||
watch::knownSettingsFiles,
|
||||
watch::readSettingsContent,
|
||||
watch::normalizeSettingsContent
|
||||
);
|
||||
configHotloadEngine.configure(500L, 3_000L, List.of(watch.settingsFile()), List.of());
|
||||
// Stale-temp cleanup must complete before services enable: StudioSVC.onEnable downloads
|
||||
// packs through cache/temp on an async thread, and a concurrent delete of that folder
|
||||
// truncated pack imports mid-copy (partial packs/<key> without dimensions/).
|
||||
@@ -714,7 +705,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
pendingWorldReplacements.captureVanillaLevelContext();
|
||||
pendingWorldReplacements.verifyLoadedPublishedWorlds();
|
||||
J.a(this::bstats);
|
||||
J.ar(() -> settingsHotloadWatch.checkConfigHotload(configHotloadEngine), 10);
|
||||
J.ar(watch::checkConfigHotload, 10);
|
||||
J.sr(this::tickQueue, 0);
|
||||
J.s(this::setupPapi);
|
||||
if (IrisStartupValidation.isReady()) {
|
||||
@@ -883,9 +874,10 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
BukkitPlatform.hudBar().shutdown();
|
||||
BukkitPlatform.hudLanes().shutdown();
|
||||
}
|
||||
if (configHotloadEngine != null) {
|
||||
configHotloadEngine.clear();
|
||||
configHotloadEngine = null;
|
||||
SettingsHotloadWatch activeSettingsHotloadWatch = settingsHotloadWatch;
|
||||
settingsHotloadWatch = null;
|
||||
if (activeSettingsHotloadWatch != null) {
|
||||
activeSettingsHotloadWatch.close();
|
||||
}
|
||||
// super.onDisable() cancels plugin tasks and unregisters every listener.
|
||||
super.onDisable();
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2026 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.core;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.volmlib.util.hotload.ConfigHotloadEngine;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Identity and hotload handling for settings.json. Supplies the predicates the
|
||||
* {@link ConfigHotloadEngine} is built from and drains the touched-file queue.
|
||||
*/
|
||||
public final class SettingsHotloadWatch {
|
||||
private static final int MAX_SETTINGS_BYTES = 2 * 1024 * 1024;
|
||||
|
||||
private final File settingsFile;
|
||||
|
||||
public SettingsHotloadWatch(File settingsFile) {
|
||||
this.settingsFile = settingsFile;
|
||||
}
|
||||
|
||||
public File settingsFile() {
|
||||
return settingsFile;
|
||||
}
|
||||
|
||||
public void checkConfigHotload(ConfigHotloadEngine engine) {
|
||||
if (engine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ConfigHotloadEngine.StableContentSnapshot snapshot : engine.pollTouchedSnapshots()) {
|
||||
if ("missing".equals(snapshot.signature())) {
|
||||
engine.processSnapshotChange(snapshot, ignored -> true, null);
|
||||
Iris.warn("settings.json was removed; retaining the last valid runtime settings.");
|
||||
continue;
|
||||
}
|
||||
engine.processSnapshotChange(
|
||||
snapshot,
|
||||
stable -> applySettingsSnapshot(stable.file(), stable.normalizedContent()),
|
||||
ignored -> Iris.info("Hotloaded settings.json ")
|
||||
);
|
||||
}
|
||||
IrisLanguage.update();
|
||||
}
|
||||
|
||||
public boolean isSettingsFile(File file) {
|
||||
if (file == null || settingsFile == null) {
|
||||
return false;
|
||||
}
|
||||
return settingsFile.getAbsoluteFile().equals(file.getAbsoluteFile());
|
||||
}
|
||||
|
||||
public List<File> knownSettingsFiles() {
|
||||
if (settingsFile == null) {
|
||||
return List.of();
|
||||
}
|
||||
return List.of(settingsFile);
|
||||
}
|
||||
|
||||
public String readSettingsContent(File file) {
|
||||
if (file == null || !file.exists() || !file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream input = Files.newInputStream(file.toPath())) {
|
||||
byte[] content = input.readNBytes(MAX_SETTINGS_BYTES + 1);
|
||||
if (content.length > MAX_SETTINGS_BYTES) {
|
||||
throw new IOException("Settings exceed " + MAX_SETTINGS_BYTES + " bytes: " + file);
|
||||
}
|
||||
return new String(content, StandardCharsets.UTF_8);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
public String normalizeSettingsContent(String text) {
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return text.replace("\r\n", "\n").trim();
|
||||
}
|
||||
|
||||
private boolean applySettingsSnapshot(File file, String content) {
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return IrisSettings.applyHotloadSnapshot(content, IrisLanguage::reload);
|
||||
} catch (RuntimeException failure) {
|
||||
Iris.warn("Rejected invalid settings hotload from %s: %s",
|
||||
file.getAbsolutePath(),
|
||||
failure.getMessage() == null ? failure.getClass().getSimpleName() : failure.getMessage());
|
||||
Iris.reportError(failure);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-85
@@ -18,121 +18,45 @@
|
||||
|
||||
package art.arcane.iris.modded.service;
|
||||
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.iris.core.SettingsHotloadWatch;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.volmlib.util.hotload.ConfigHotloadEngine;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class ModdedSettingsHotloadService implements ModdedTickableService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
|
||||
private static final long POLL_PERIOD_MILLIS = 500L;
|
||||
private static final long HOTLOAD_COOLDOWN_MILLIS = 3_000L;
|
||||
private static final int MAX_SETTINGS_BYTES = 2 * 1024 * 1024;
|
||||
|
||||
private ConfigHotloadEngine hotloadEngine;
|
||||
private SettingsHotloadWatch hotloadWatch;
|
||||
private long lastPollAtNanos;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
File settingsFile = settingsFile();
|
||||
hotloadEngine = new ConfigHotloadEngine(
|
||||
this::isSettingsFile,
|
||||
() -> List.of(settingsFile),
|
||||
this::readSettings,
|
||||
this::normalizeSettings
|
||||
);
|
||||
hotloadEngine.configure(
|
||||
POLL_PERIOD_MILLIS,
|
||||
HOTLOAD_COOLDOWN_MILLIS,
|
||||
List.of(settingsFile),
|
||||
List.of()
|
||||
);
|
||||
hotloadWatch = new SettingsHotloadWatch(settingsFile());
|
||||
lastPollAtNanos = 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
ConfigHotloadEngine active = hotloadEngine;
|
||||
hotloadEngine = null;
|
||||
SettingsHotloadWatch active = hotloadWatch;
|
||||
hotloadWatch = null;
|
||||
if (active != null) {
|
||||
active.clear();
|
||||
active.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerTick(MinecraftServer server) {
|
||||
ConfigHotloadEngine active = hotloadEngine;
|
||||
SettingsHotloadWatch active = hotloadWatch;
|
||||
if (active == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long now = System.nanoTime();
|
||||
if (now - lastPollAtNanos < TimeUnit.MILLISECONDS.toNanos(POLL_PERIOD_MILLIS)) {
|
||||
if (now - lastPollAtNanos < TimeUnit.MILLISECONDS.toNanos(SettingsHotloadWatch.POLL_PERIOD_MILLIS)) {
|
||||
return;
|
||||
}
|
||||
lastPollAtNanos = now;
|
||||
|
||||
try {
|
||||
for (ConfigHotloadEngine.StableContentSnapshot snapshot : active.pollTouchedSnapshots()) {
|
||||
if ("missing".equals(snapshot.signature())) {
|
||||
active.processSnapshotChange(snapshot, ignored -> true, null);
|
||||
LOGGER.warn("settings.json was removed; retaining the last valid runtime settings");
|
||||
continue;
|
||||
}
|
||||
active.processSnapshotChange(snapshot, this::reloadSettings, ignored -> LOGGER.info("Hotloaded settings.json"));
|
||||
}
|
||||
IrisLanguage.update();
|
||||
} catch (RuntimeException failure) {
|
||||
LOGGER.error("Iris settings hotload watcher failed", failure);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean reloadSettings(ConfigHotloadEngine.StableContentSnapshot snapshot) {
|
||||
try {
|
||||
String content = snapshot.normalizedContent();
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
return IrisSettings.applyHotloadSnapshot(content, IrisLanguage::reload);
|
||||
} catch (RuntimeException failure) {
|
||||
LOGGER.error("Iris settings hotload failed; keeping the previous runtime settings", failure);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSettingsFile(File file) {
|
||||
return file != null && settingsFile().getAbsoluteFile().equals(file.getAbsoluteFile());
|
||||
}
|
||||
|
||||
private String readSettings(File file) {
|
||||
if (file == null || !file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
try (InputStream input = Files.newInputStream(file.toPath())) {
|
||||
byte[] content = input.readNBytes(MAX_SETTINGS_BYTES + 1);
|
||||
if (content.length > MAX_SETTINGS_BYTES) {
|
||||
throw new IOException("Settings exceed " + MAX_SETTINGS_BYTES + " bytes: " + file);
|
||||
}
|
||||
return new String(content, StandardCharsets.UTF_8);
|
||||
} catch (IOException failure) {
|
||||
throw new UncheckedIOException("Failed to read Iris settings from " + file, failure);
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeSettings(String content) {
|
||||
return content == null ? null : content.replace("\r\n", "\n").trim();
|
||||
active.checkConfigHotload();
|
||||
}
|
||||
|
||||
private static File settingsFile() {
|
||||
|
||||
Reference in New Issue
Block a user