Accurate teleport failures

- Waits for the destination chunk without a wall-clock deadline so a cold first chunk no longer fails the teleport
- Reports the failure cause, an unloaded dimension, an offline player or a refused teleport separately
- Adds the new messages to every locale
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-29 06:50:24 -04:00
1 parent 5cadd9b8c4
commit 82fa69611e
20 files changed
+147 -5

No files matched your search

@@ -60,6 +60,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import art.arcane.volmlib.util.localization.MessageArgument;
@@ -132,8 +133,7 @@ public final class IrisModdedCommands {
String dimensionId = level.name();
NativeModdedServer server = source.server();
CompletableFuture<Boolean> teleport = NativeWorldTeleport.teleport(player, new NativeWorldTeleport.Destination(
server, ModdedDimensionManager.level(server, dimensionId), 8.5D, Double.MIN_VALUE, 8.5D,
System.nanoTime() + TimeUnit.SECONDS.toNanos(10L)));
server, ModdedDimensionManager.level(server, dimensionId), 8.5D, Double.MIN_VALUE, 8.5D, 0L));
teleport.whenComplete((success, failure) -> {
if (Boolean.TRUE.equals(success) && failure == null) {
return;
@@ -142,14 +142,42 @@ public final class IrisModdedCommands {
ModdedIrisLog.error("Iris teleport into '{}' failed for {}",
dimensionId, player.id(), failure);
}
server.execute(() -> fail(source, IrisLanguage.plain(
ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_DIMENSION_IS_NOT_LOADED,
MessageArgument.untrusted("dimensionId", dimensionId))));
server.execute(() -> fail(source, teleportFailure(dimensionId, player.name(), failure,
ModdedDimensionManager.level(server, dimensionId) != null, server.player(player.id()) != null)));
});
ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTING, MessageArgument.untrusted("value", player.name()), MessageArgument.untrusted("dimensionId", dimensionId)));
return 1;
}
static String teleportFailure(String dimensionId, String playerName, Throwable failure,
boolean dimensionLoaded, boolean playerOnline) {
if (failure != null) {
return IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_REASON,
MessageArgument.untrusted("dimensionId", dimensionId),
MessageArgument.untrusted("reason", failureReason(failure)));
}
if (!dimensionLoaded) {
return IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_DIMENSION_IS_NOT_LOADED,
MessageArgument.untrusted("dimensionId", dimensionId));
}
if (!playerOnline) {
return IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_CANCELLED_PLAYER_OFFLINE,
MessageArgument.untrusted("dimensionId", dimensionId),
MessageArgument.untrusted("value", playerName));
}
return IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_REFUSED,
MessageArgument.untrusted("dimensionId", dimensionId));
}
private static String failureReason(Throwable failure) {
Throwable cause = failure;
while (cause instanceof CompletionException && cause.getCause() != null) {
cause = cause.getCause();
}
String message = cause.getMessage();
return message == null || message.isBlank() ? cause.getClass().getSimpleName() : message;
}
static int evacuate(NativeCommandSource source, NativeWorld target) {
NativeModdedServer server = source.server();
NativeWorld level = target != null ? target : source.world();
@@ -71,6 +71,18 @@ public final class ModdedCommandMessages {
"iris.modded.irismoddedcommands.teleport_failed_dimension_is_not_loaded",
"Teleport failed: dimension " + "{dimensionId}" + " is not loaded."
);
public static final TextKey IRIS_MODDED_COMMANDS_TELEPORT_FAILED_REASON = TextKey.of(
"iris.modded.irismoddedcommands.teleport_failed_reason",
"Teleport into " + "{dimensionId}" + " failed: " + "{reason}"
);
public static final TextKey IRIS_MODDED_COMMANDS_TELEPORT_CANCELLED_PLAYER_OFFLINE = TextKey.of(
"iris.modded.irismoddedcommands.teleport_cancelled_player_offline",
"Teleport into " + "{dimensionId}" + " cancelled: " + "{value}" + " is no longer online."
);
public static final TextKey IRIS_MODDED_COMMANDS_TELEPORT_REFUSED = TextKey.of(
"iris.modded.irismoddedcommands.teleport_refused",
"Teleport into " + "{dimensionId}" + " was refused by the server."
);
public static final TextKey IRIS_MODDED_COMMANDS_TELEPORTING = TextKey.of(
"iris.modded.irismoddedcommands.teleporting",
"Teleporting " + "{value}" + " to " + "{dimensionId}" + "..."
@@ -1266,6 +1278,9 @@ public final class ModdedCommandMessages {
IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_PLAYER_IRIS_TP_DIMENSION_PLAYER,
IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS,
IRIS_MODDED_COMMANDS_TELEPORT_FAILED_DIMENSION_IS_NOT_LOADED,
IRIS_MODDED_COMMANDS_TELEPORT_FAILED_REASON,
IRIS_MODDED_COMMANDS_TELEPORT_CANCELLED_PLAYER_OFFLINE,
IRIS_MODDED_COMMANDS_TELEPORT_REFUSED,
IRIS_MODDED_COMMANDS_TELEPORTING,
IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_2,
IRIS_MODDED_COMMANDS_CANNOT_EVACUATE_PRIMARY_WORLD_THERE_IS_NOWHERE_SEND_PLAYERS,
@@ -0,0 +1,48 @@
package art.arcane.iris.modded.command;
import art.arcane.iris.localization.IrisLanguage;
import art.arcane.iris.modded.localization.ModdedCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import org.junit.Test;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException;
import static org.junit.Assert.assertEquals;
public class IrisModdedCommandsTeleportTest {
private static final String DIMENSION = "irisworldgen:1";
@Test
public void failureReportsItsRootCauseInsteadOfAnUnloadedDimension() {
String message = IrisModdedCommands.teleportFailure(DIMENSION, "Tester",
new CompletionException(new IllegalStateException("Exception generating new chunk")), true, true);
assertEquals(IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_REASON,
MessageArgument.untrusted("dimensionId", DIMENSION),
MessageArgument.untrusted("reason", "Exception generating new chunk")), message);
}
@Test
public void failureWithoutMessageNamesTheFailureType() {
String message = IrisModdedCommands.teleportFailure(DIMENSION, "Tester", new TimeoutException(), true, true);
assertEquals(IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_REASON,
MessageArgument.untrusted("dimensionId", DIMENSION),
MessageArgument.untrusted("reason", "TimeoutException")), message);
}
@Test
public void unloadedDimensionIsReportedOnlyWhenItIsGone() {
assertEquals(IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_DIMENSION_IS_NOT_LOADED,
MessageArgument.untrusted("dimensionId", DIMENSION)),
IrisModdedCommands.teleportFailure(DIMENSION, "Tester", null, false, true));
assertEquals(IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_CANCELLED_PLAYER_OFFLINE,
MessageArgument.untrusted("dimensionId", DIMENSION),
MessageArgument.untrusted("value", "Tester")),
IrisModdedCommands.teleportFailure(DIMENSION, "Tester", null, true, false));
assertEquals(IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_REFUSED,
MessageArgument.untrusted("dimensionId", DIMENSION)),
IrisModdedCommands.teleportFailure(DIMENSION, "Tester", null, true, true));
}
}