This commit is contained in:
Brian Neumann-Fopiano
2026-08-13 12:38:08 -04:00
parent 14b6280668
commit 1586b4bd1a
68 changed files with 733 additions and 211 deletions
@@ -613,18 +613,16 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
// (listeners, shutdown hook, replacement journals) are the safety-critical ones.
// Only services that actually enabled get listeners and a later onDisable.
enabledServices.clear();
IrisService firstFailedService = null;
for (IrisService service : orderedServices) {
try {
service.onEnable();
enabledServices.add(service);
} catch (Throwable e) {
if (firstFailedService == null) {
firstFailedService = service;
}
// A service failure is NOT a datapack validation failure: the admission gate
// must never lock every login over a broken cosmetic service. Log loudly,
// continue degraded, and clean up whatever the partial onEnable started
// (a failed service is excluded from the teardown loop).
Iris.reportError("Failed to enable " + service.getClass().getSimpleName() + "; continuing with a degraded runtime.", e);
// A failed service is excluded from the teardown loop, so clean up whatever
// its partial onEnable started right here, best-effort.
try {
service.onDisable();
} catch (Throwable cleanup) {
@@ -632,11 +630,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
}
}
}
if (firstFailedService != null) {
IrisStartupValidation.markDatapacksInvalid("Iris service "
+ firstFailedService.getClass().getSimpleName()
+ " failed to enable; world creation and player admission are locked. Check the log above.");
}
for (IrisService service : enabledServices) {
try {
registerListener(service);
@@ -468,7 +468,21 @@ public final class PendingWorldReplacementManager implements Listener {
}
private List<Transaction> loadTransactions() throws IOException {
return WorldReplacementJournal.load(dataDirectory(), IrisWorldStorage.levelRoot().toPath());
Path levelRoot = IrisWorldStorage.levelRoot().toPath();
List<Transaction> transactions = WorldReplacementJournal.load(dataDirectory(), levelRoot);
List<Transaction> applicable = new ArrayList<>(transactions.size());
for (Transaction transaction : transactions) {
// A journal staged against another level root is not this server's transaction;
// skip it (the bootstrap already told the operator how to resolve it).
if (WorldReplacementJournal.appliesTo(transaction, levelRoot)) {
applicable.add(transaction);
} else {
Iris.warn("Ignoring pending world replacement " + transaction.id() + " for "
+ transaction.worldKey() + "; it was staged against level root "
+ transaction.levelRoot() + ".");
}
}
return applicable;
}
private void writeTransaction(Transaction transaction) throws IOException {
@@ -263,6 +263,10 @@ public class CommandPack implements DirectorExecutor {
for (File packDirectory : packDirectories) {
PackValidationResult result = PackValidationRegistry.get(packDirectory.getName());
if (result == null) {
// The boot cache only loads when it covers every pack, so a partial write is
// pointless - but say so instead of silently skipping the persist forever.
Iris.warn("Pack validation cache not written: \"" + packDirectory.getName()
+ "\" has no registered result. Run /iris pack validate all to repopulate it.");
return;
}
results.add(result);
@@ -329,8 +329,7 @@ public class CommandStudio implements DirectorExecutor {
return;
}
if (radius <= 0 || radius > 2048) {
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_ONLY_WORKS_IRIS_WORLD));
sender().sendMessage("Radius must be between 1 and 2048 chunks.");
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_REGIONS_RADIUS_OUT_OF_RANGE));
return;
}
var sender = sender();
@@ -393,7 +392,8 @@ public class CommandStudio implements DirectorExecutor {
data.forEach((k, v) -> sender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_STUDIO_MESSAGE, MessageArgument.untrusted("k", k), MessageArgument.untrusted("value", loader.load(k).getRarity()), MessageArgument.untrusted("value2", Form.f((double) v.get() / totalTasks * 100, 2)))));
} catch (Throwable e) {
Iris.reportError(e);
sender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_ONLY_WORKS_IRIS_WORLD));
sender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_REGIONS_SCAN_FAILED,
MessageArgument.untrusted("value", String.valueOf(e.getMessage()))));
} finally {
if (c != -1) {
J.car(c);
@@ -712,7 +712,7 @@ public class CommandStudio implements DirectorExecutor {
}
}
@Director(aliases = "find-objects", description = "Capture an IGenData chunk report for nearby chunks", descriptionKey = "iris.director.commandstudio.director.capture_igendata_chunk_report_nearby_chunks")
@Director(aliases = "find-objects", description = "Capture an IGenData chunk report for nearby chunks", descriptionKey = "iris.director.commandstudio.director.capture_igendata_chunk_report_nearby_chunks", origin = DirectorOrigin.PLAYER)
public void objects() {
if (!IrisToolbelt.isIrisWorld(player().getWorld())) {
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_STUDIO_YOU_MUST_BE_IRIS_WORLD));