mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Bounded teleport deadline
- /iris tp waits up to 120 seconds for the destination chunk - A refusing generator or removed dimension still fails the command - Tests cover the deadline and the reported timeout limit
This commit is contained in:
+5
-1
@@ -67,6 +67,9 @@ import art.arcane.volmlib.util.localization.MessageArgument;
|
||||
|
||||
public final class IrisModdedCommands {
|
||||
private static final long DOWNLOAD_SHUTDOWN_POLL_SECONDS = 15L;
|
||||
// The first chunk of a cold dimension also loads the pack and binds the engine; the deadline only bounds a
|
||||
// destination whose chunk never completes (a refusing generator or a removed dimension).
|
||||
private static final long TELEPORT_DEADLINE_SECONDS = 120L;
|
||||
private static final Object DOWNLOAD_MONITOR = new Object();
|
||||
|
||||
static final SuggestionProvider<NativeCommandSource> PACK_NAMES = ModdedCommandSuggestions.PACK_NAMES;
|
||||
@@ -133,7 +136,8 @@ 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, 0L));
|
||||
server, ModdedDimensionManager.level(server, dimensionId), 8.5D, Double.MIN_VALUE, 8.5D,
|
||||
System.nanoTime() + TimeUnit.SECONDS.toNanos(TELEPORT_DEADLINE_SECONDS)));
|
||||
teleport.whenComplete((success, failure) -> {
|
||||
if (Boolean.TRUE.equals(success) && failure == null) {
|
||||
return;
|
||||
|
||||
+97
@@ -1,18 +1,115 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import art.arcane.iris.localization.IrisLanguage;
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
import art.arcane.iris.modded.ModdedDimensionManager;
|
||||
import art.arcane.iris.modded.localization.ModdedCommandMessages;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldTeleport;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeWorld;
|
||||
import art.arcane.volmlib.util.localization.MessageArgument;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class IrisModdedCommandsTeleportTest {
|
||||
private static final String DIMENSION = "irisworldgen:1";
|
||||
|
||||
@BeforeClass
|
||||
public static void bootstrap() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void teleportWaitsForAColdDestinationWithinABoundedDeadline() {
|
||||
NativeModdedServer server = mock(NativeModdedServer.class);
|
||||
NativeCommandSource source = mock(NativeCommandSource.class);
|
||||
when(source.server()).thenReturn(server);
|
||||
NativeWorld level = mock(NativeWorld.class);
|
||||
when(level.name()).thenReturn(DIMENSION);
|
||||
NativeProtocolPlayer player = mock(NativeProtocolPlayer.class);
|
||||
when(player.name()).thenReturn("Tester");
|
||||
AtomicReference<NativeWorldTeleport.Destination> requested = new AtomicReference<>();
|
||||
|
||||
long before = System.nanoTime();
|
||||
try (MockedStatic<NativeWorldGenerators> generators = mockStatic(NativeWorldGenerators.class);
|
||||
MockedStatic<ModdedDimensionManager> dimensions = mockStatic(ModdedDimensionManager.class);
|
||||
MockedStatic<NativeWorldTeleport> teleport = mockStatic(NativeWorldTeleport.class)) {
|
||||
generators.when(() -> NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class))
|
||||
.thenReturn(mock(IrisModdedChunkGenerator.class));
|
||||
dimensions.when(() -> ModdedDimensionManager.level(server, DIMENSION)).thenReturn(level);
|
||||
teleport.when(() -> NativeWorldTeleport.teleport(any(), any())).thenAnswer((InvocationOnMock invocation) -> {
|
||||
requested.set(invocation.getArgument(1));
|
||||
return new CompletableFuture<Boolean>();
|
||||
});
|
||||
|
||||
assertEquals(1, IrisModdedCommands.tp(source, level, player));
|
||||
}
|
||||
long after = System.nanoTime();
|
||||
|
||||
long deadline = requested.get().deadlineNanos();
|
||||
assertTrue("deadline must outlast a cold first chunk", deadline - before >= TimeUnit.SECONDS.toNanos(60L));
|
||||
assertTrue("deadline must be bounded", deadline - after <= TimeUnit.SECONDS.toNanos(120L));
|
||||
}
|
||||
|
||||
@Test(timeout = 15_000L)
|
||||
public void expiredTeleportReportsTheCallersDeadline() throws Exception {
|
||||
MinecraftServer server = mock(MinecraftServer.class);
|
||||
when(server.isSameThread()).thenReturn(true);
|
||||
ResourceKey<Level> dimension = ResourceKey.create(Registries.DIMENSION, Identifier.parse(DIMENSION));
|
||||
ServerChunkCache chunks = mock(ServerChunkCache.class);
|
||||
when(chunks.addTicketAndLoadWithRadius(any(), any(), anyInt())).thenReturn(new CompletableFuture<>());
|
||||
ServerLevel serverLevel = mock(ServerLevel.class);
|
||||
when(serverLevel.getServer()).thenReturn(server);
|
||||
when(serverLevel.dimension()).thenReturn(dimension);
|
||||
when(serverLevel.getChunkSource()).thenReturn(chunks);
|
||||
when(server.getLevel(dimension)).thenReturn(serverLevel);
|
||||
ServerPlayer serverPlayer = mock(ServerPlayer.class);
|
||||
when(serverPlayer.getUUID()).thenReturn(UUID.randomUUID());
|
||||
NativeWorld level = mock(NativeWorld.class);
|
||||
when(level.nativeHandle()).thenReturn(serverLevel);
|
||||
|
||||
CompletableFuture<Boolean> teleport = NativeWorldTeleport.teleport(NativeProtocolPlayer.fromHandle(serverPlayer),
|
||||
new NativeWorldTeleport.Destination(NativeModdedServer.fromHandle(server), level, 8.5D,
|
||||
Double.MIN_VALUE, 8.5D, System.nanoTime() + TimeUnit.SECONDS.toNanos(2L)));
|
||||
|
||||
ExecutionException failure = assertThrows(ExecutionException.class, () -> teleport.get(10L, TimeUnit.SECONDS));
|
||||
assertTrue(failure.getCause() instanceof TimeoutException);
|
||||
assertTrue(failure.getCause().getMessage(), failure.getCause().getMessage().contains("within 2 seconds"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureReportsItsRootCauseInsteadOfAnUnloadedDimension() {
|
||||
String message = IrisModdedCommands.teleportFailure(DIMENSION, "Tester",
|
||||
|
||||
Reference in New Issue
Block a user