diff --git a/src/main/java/autismclient/util/macro/MacroExecutor.java b/src/main/java/autismclient/util/macro/MacroExecutor.java index f95aed9..f959341 100644 --- a/src/main/java/autismclient/util/macro/MacroExecutor.java +++ b/src/main/java/autismclient/util/macro/MacroExecutor.java @@ -522,7 +522,7 @@ public class MacroExecutor { ACTIVE_RUN_LIST = java.util.List.copyOf(ACTIVE_RUNS.values()); } - private static java.util.List activeRunSnapshot() { + static java.util.List activeRunSnapshot() { return ACTIVE_RUN_LIST; } @@ -918,7 +918,7 @@ public class MacroExecutor { backgroundMoves.clear(); reconcilePersistentInput(Minecraft.getInstance()); MacroConditionRegistry.cancelAll(); - drainOneShotListeners(); + MacroPacketObserve.drainOneShotListeners(); isWaitingForChat.set(false); isWaitingForPacket.set(false); synchronized (packetWaitLock) { @@ -976,10 +976,10 @@ public class MacroExecutor { return isRunning.get() || activePacketFuture != null || isWaitingForPacket.get() - || !ONE_SHOT_RECV.isEmpty() - || !ONE_SHOT_SEND.isEmpty() - || !TIMED_ONE_SHOT_RECV.isEmpty() - || !TIMED_ONE_SHOT_SEND.isEmpty(); + || !MacroPacketObserve.ONE_SHOT_RECV.isEmpty() + || !MacroPacketObserve.ONE_SHOT_SEND.isEmpty() + || !MacroPacketObserve.TIMED_ONE_SHOT_RECV.isEmpty() + || !MacroPacketObserve.TIMED_ONE_SHOT_SEND.isEmpty(); } public static boolean isMacroRunning(String macroName) { @@ -1121,7 +1121,7 @@ public class MacroExecutor { } } - private static final long ONE_SHOT_EXPIRE_NANOS = java.util.concurrent.TimeUnit.MINUTES.toNanos(10); + static final long ONE_SHOT_EXPIRE_NANOS = java.util.concurrent.TimeUnit.MINUTES.toNanos(10); public static final class OneShotPacketListener { public final java.util.concurrent.CompletableFuture> future = new java.util.concurrent.CompletableFuture<>(); @@ -1153,218 +1153,31 @@ public class MacroExecutor { } } - static final class TimedOneShotPacketListener { - public final java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); - private final java.util.List registry; - private final TimedPacketMatch match; - private final long expireAtNanos; - TimedOneShotPacketListener(java.util.List registry, TimedPacketMatch match) { - this.registry = registry; - this.match = match; - this.expireAtNanos = System.nanoTime() + ONE_SHOT_EXPIRE_NANOS; - registry.add(this); - } - - boolean offer(MacroEvent event) { - if (future.isDone()) return true; - if (event.nanoTime() - expireAtNanos >= 0L) { future.cancel(true); return true; } - if (match.test(event.packet(), event.direction(), event.nanoTime())) { - future.complete(event); - return true; - } - return false; - } - public void cancel() { - registry.remove(this); - if (!future.isDone()) future.cancel(true); - } - } - - interface TimedPacketMatch { - boolean test(Packet packet, String direction, long nanoTime); - } - - private static final java.util.List ONE_SHOT_RECV = - new java.util.concurrent.CopyOnWriteArrayList<>(); - private static final java.util.List ONE_SHOT_SEND = - new java.util.concurrent.CopyOnWriteArrayList<>(); - private static final java.util.List TIMED_ONE_SHOT_RECV = - new java.util.concurrent.CopyOnWriteArrayList<>(); - private static final java.util.List TIMED_ONE_SHOT_SEND = - new java.util.concurrent.CopyOnWriteArrayList<>(); - public static OneShotPacketListener awaitReceive(java.util.function.Predicate> match) { - return new OneShotPacketListener(ONE_SHOT_RECV, match); + return MacroPacketObserve.awaitReceive(match); } public static OneShotPacketListener awaitSend(java.util.function.Predicate> match) { - return new OneShotPacketListener(ONE_SHOT_SEND, match); - } - - static TimedOneShotPacketListener awaitTimedReceive(TimedPacketMatch match) { - return new TimedOneShotPacketListener(TIMED_ONE_SHOT_RECV, match); - } - - static TimedOneShotPacketListener awaitTimedSend(java.util.function.Predicate> match) { - return awaitTimedSend((packet, direction, nanoTime) -> match.test(packet)); - } - - static TimedOneShotPacketListener awaitTimedSend(TimedPacketMatch match) { - return new TimedOneShotPacketListener(TIMED_ONE_SHOT_SEND, match); - } - - private static void fireOneShotObservers(java.util.List list, Packet packet) { - if (list.isEmpty()) return; - - long now = System.nanoTime(); - java.util.ArrayList matched = null; - for (OneShotPacketListener listener : list) { - boolean drop; - try { drop = listener.offer(packet, now); } - catch (Throwable ignored) { drop = true; } - if (drop) { - if (matched == null) matched = new java.util.ArrayList<>(4); - matched.add(listener); - } - } - if (matched != null) list.removeAll(matched); - } - - private static void fireTimedObservers(java.util.List list, MacroEvent event) { - if (list.isEmpty()) return; - java.util.ArrayList matched = null; - for (TimedOneShotPacketListener listener : list) { - boolean drop; - try { drop = listener.offer(event); } - catch (Throwable ignored) { drop = true; } - if (drop) { - if (matched == null) matched = new java.util.ArrayList<>(4); - matched.add(listener); - } - } - if (matched != null) list.removeAll(matched); - } - - private static void drainOneShotListeners() { - for (OneShotPacketListener listener : ONE_SHOT_RECV) listener.cancel(); - for (OneShotPacketListener listener : ONE_SHOT_SEND) listener.cancel(); - for (TimedOneShotPacketListener listener : TIMED_ONE_SHOT_RECV) listener.cancel(); - for (TimedOneShotPacketListener listener : TIMED_ONE_SHOT_SEND) listener.cancel(); + return MacroPacketObserve.awaitSend(match); } public static String oneShotCensus() { - return "runs=" + getActiveRunCount() - + " recv=" + ONE_SHOT_RECV.size() - + " send=" + ONE_SHOT_SEND.size() - + " timedRecv=" + TIMED_ONE_SHOT_RECV.size() - + " timedSend=" + TIMED_ONE_SHOT_SEND.size(); + return MacroPacketObserve.oneShotCensus(); } public static void onPacketSent(Packet packet) { - if (PackHideState.isHardLocked()) return; - if (!TIMED_ONE_SHOT_SEND.isEmpty()) { - fireTimedObservers(TIMED_ONE_SHOT_SEND, new MacroEvent(packet, "C2S", System.nanoTime(), null)); - } - fireOneShotObservers(ONE_SHOT_SEND, packet); - onPacketObserved(packet, "C2S"); + MacroPacketObserve.onPacketSent(packet); } public static void onPacketReceived(Packet packet) { - if (PackHideState.isHardLocked()) return; - if (!TIMED_ONE_SHOT_RECV.isEmpty()) { - fireTimedObservers(TIMED_ONE_SHOT_RECV, new MacroEvent(packet, "S2C", System.nanoTime(), null)); - } - fireOneShotObservers(ONE_SHOT_RECV, packet); - - if (packet instanceof net.minecraft.network.protocol.game.ClientboundRespawnPacket - || packet instanceof net.minecraft.network.protocol.game.ClientboundLoginPacket) { - MacroConditionRegistry.onRespawnPacket(); - } - if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket) { - MacroConditionRegistry.onTeleportPacket(); - } - onPacketObserved(packet, "S2C"); + MacroPacketObserve.onPacketReceived(packet); } public static void onPacketObserved(Packet packet, String direction) { - if (PackHideState.isHardLocked()) return; - MacroChatObserve.syncRecentChatServerContext(); - - java.util.List runs = activeRunSnapshot(); - boolean s2c = "S2C".equals(direction); - boolean chatWork = false; - boolean mineWork = false; - boolean packetWork = false; - for (int i = 0; i < runs.size(); i++) { - RunState run = runs.get(i); - if (!run.running.get()) continue; - if (run.waitingForChat.get()) chatWork = true; - if (run.activeMineTracker != null) mineWork = true; - if (run.waitingForPacket.get()) packetWork = true; - } - - if (s2c && (chatWork || isWaitingForChat.get())) { - ChatCapture capture = MacroChatObserve.extractIncomingChat(packet); - if (capture != null && capture.message() != null && !capture.message().isBlank()) { - - for (RunState run : runs) { - var matched = run.running.get() && run.waitingForChat.get() - ? MacroChatObserve.captureChatAction(run.waitingChatAction, capture, run.variables) - : java.util.Optional.>empty(); - if (matched.isPresent()) { - run.stagedCaptures.put(run.waitingChatAction, matched.get()); - CompletableFuture f = run.chatFuture; - if (f != null) f.complete(null); - } - } - if (isCurrentRunActive() && isWaitingForChat.get() && MacroChatObserve.matchesWaitingChat(capture)) { - lastMatchedChat.set(capture.displayText()); - CompletableFuture f = activeChatFuture; - if (f != null) f.complete(null); - } - } - } - - if (s2c && mineWork) { - Minecraft mc = Minecraft.getInstance(); - for (RunState run : runs) { - MacroWorldActions.MineProgressTracker tracker = run.running.get() ? run.activeMineTracker : null; - if (tracker != null) tracker.observePacket(mc, packet); - } - } - - if (!isCurrentRunActive()) return; - - if (packetWork) { - for (RunState run : runs) { - if (!run.running.get() || !run.waitingForPacket.get()) continue; - CompletableFuture f = null; - synchronized (run.packetWaitLock) { - if (run.waitingForPacket.get() && MacroPacketWait.matchesPacketTarget(run.waitingPacketName.get(), packet, direction)) { - run.lastMatchedPacket = packet; - run.lastMatchedPacketDirection = direction; - f = run.packetFuture; - } - } - if (f != null) f.complete(null); - } - } - - if (isWaitingForPacket.get()) { - CompletableFuture f = null; - synchronized (packetWaitLock) { - if (!isWaitingForPacket.get()) return; - - String target = waitingPacketName.get(); - if (MacroPacketWait.matchesPacketTarget(target, packet, direction)) { - lastReceivedPacket.set(direction + " " + AutismPacketNamer.getFriendlyName(packet, direction)); - f = activePacketFuture; - } - } - if (f != null) f.complete(null); - } + MacroPacketObserve.onPacketObserved(packet, direction); } + public static void observeIncomingChat(Packet packet) { MacroChatObserve.observeIncomingChat(packet); } diff --git a/src/main/java/autismclient/util/macro/MacroPacketObserve.java b/src/main/java/autismclient/util/macro/MacroPacketObserve.java new file mode 100644 index 0000000..4c7914d --- /dev/null +++ b/src/main/java/autismclient/util/macro/MacroPacketObserve.java @@ -0,0 +1,224 @@ +package autismclient.util.macro; + +import autismclient.modules.PackHideState; +import autismclient.util.AutismPacketNamer; +import net.minecraft.client.Minecraft; +import net.minecraft.network.protocol.Packet; + +import java.util.concurrent.CompletableFuture; + +final class MacroPacketObserve { + private MacroPacketObserve() { + } + static final class TimedOneShotPacketListener { + public final java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + private final java.util.List registry; + private final TimedPacketMatch match; + private final long expireAtNanos; + TimedOneShotPacketListener(java.util.List registry, TimedPacketMatch match) { + this.registry = registry; + this.match = match; + this.expireAtNanos = System.nanoTime() + MacroExecutor.ONE_SHOT_EXPIRE_NANOS; + registry.add(this); + } + + boolean offer(MacroExecutor.MacroEvent event) { + if (future.isDone()) return true; + if (event.nanoTime() - expireAtNanos >= 0L) { future.cancel(true); return true; } + if (match.test(event.packet(), event.direction(), event.nanoTime())) { + future.complete(event); + return true; + } + return false; + } + public void cancel() { + registry.remove(this); + if (!future.isDone()) future.cancel(true); + } + } + + interface TimedPacketMatch { + boolean test(Packet packet, String direction, long nanoTime); + } + + static final java.util.List ONE_SHOT_RECV = + new java.util.concurrent.CopyOnWriteArrayList<>(); + static final java.util.List ONE_SHOT_SEND = + new java.util.concurrent.CopyOnWriteArrayList<>(); + static final java.util.List TIMED_ONE_SHOT_RECV = + new java.util.concurrent.CopyOnWriteArrayList<>(); + static final java.util.List TIMED_ONE_SHOT_SEND = + new java.util.concurrent.CopyOnWriteArrayList<>(); + + public static MacroExecutor.OneShotPacketListener awaitReceive(java.util.function.Predicate> match) { + return new MacroExecutor.OneShotPacketListener(ONE_SHOT_RECV, match); + } + + public static MacroExecutor.OneShotPacketListener awaitSend(java.util.function.Predicate> match) { + return new MacroExecutor.OneShotPacketListener(ONE_SHOT_SEND, match); + } + + static TimedOneShotPacketListener awaitTimedReceive(TimedPacketMatch match) { + return new TimedOneShotPacketListener(TIMED_ONE_SHOT_RECV, match); + } + + static TimedOneShotPacketListener awaitTimedSend(java.util.function.Predicate> match) { + return awaitTimedSend((packet, direction, nanoTime) -> match.test(packet)); + } + + static TimedOneShotPacketListener awaitTimedSend(TimedPacketMatch match) { + return new TimedOneShotPacketListener(TIMED_ONE_SHOT_SEND, match); + } + + private static void fireOneShotObservers(java.util.List list, Packet packet) { + if (list.isEmpty()) return; + + long now = System.nanoTime(); + java.util.ArrayList matched = null; + for (MacroExecutor.OneShotPacketListener listener : list) { + boolean drop; + try { drop = listener.offer(packet, now); } + catch (Throwable ignored) { drop = true; } + if (drop) { + if (matched == null) matched = new java.util.ArrayList<>(4); + matched.add(listener); + } + } + if (matched != null) list.removeAll(matched); + } + + private static void fireTimedObservers(java.util.List list, MacroExecutor.MacroEvent event) { + if (list.isEmpty()) return; + java.util.ArrayList matched = null; + for (TimedOneShotPacketListener listener : list) { + boolean drop; + try { drop = listener.offer(event); } + catch (Throwable ignored) { drop = true; } + if (drop) { + if (matched == null) matched = new java.util.ArrayList<>(4); + matched.add(listener); + } + } + if (matched != null) list.removeAll(matched); + } + + public static void drainOneShotListeners() { + for (MacroExecutor.OneShotPacketListener listener : ONE_SHOT_RECV) listener.cancel(); + for (MacroExecutor.OneShotPacketListener listener : ONE_SHOT_SEND) listener.cancel(); + for (TimedOneShotPacketListener listener : TIMED_ONE_SHOT_RECV) listener.cancel(); + for (TimedOneShotPacketListener listener : TIMED_ONE_SHOT_SEND) listener.cancel(); + } + + public static String oneShotCensus() { + return "runs=" + MacroExecutor.getActiveRunCount() + + " recv=" + ONE_SHOT_RECV.size() + + " send=" + ONE_SHOT_SEND.size() + + " timedRecv=" + TIMED_ONE_SHOT_RECV.size() + + " timedSend=" + TIMED_ONE_SHOT_SEND.size(); + } + + public static void onPacketSent(Packet packet) { + if (PackHideState.isHardLocked()) return; + if (!TIMED_ONE_SHOT_SEND.isEmpty()) { + fireTimedObservers(TIMED_ONE_SHOT_SEND, new MacroExecutor.MacroEvent(packet, "C2S", System.nanoTime(), null)); + } + fireOneShotObservers(ONE_SHOT_SEND, packet); + onPacketObserved(packet, "C2S"); + } + + public static void onPacketReceived(Packet packet) { + if (PackHideState.isHardLocked()) return; + if (!TIMED_ONE_SHOT_RECV.isEmpty()) { + fireTimedObservers(TIMED_ONE_SHOT_RECV, new MacroExecutor.MacroEvent(packet, "S2C", System.nanoTime(), null)); + } + fireOneShotObservers(ONE_SHOT_RECV, packet); + + if (packet instanceof net.minecraft.network.protocol.game.ClientboundRespawnPacket + || packet instanceof net.minecraft.network.protocol.game.ClientboundLoginPacket) { + MacroConditionRegistry.onRespawnPacket(); + } + if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket) { + MacroConditionRegistry.onTeleportPacket(); + } + onPacketObserved(packet, "S2C"); + } + + public static void onPacketObserved(Packet packet, String direction) { + if (PackHideState.isHardLocked()) return; + MacroChatObserve.syncRecentChatServerContext(); + + java.util.List runs = MacroExecutor.activeRunSnapshot(); + boolean s2c = "S2C".equals(direction); + boolean chatWork = false; + boolean mineWork = false; + boolean packetWork = false; + for (int i = 0; i < runs.size(); i++) { + MacroExecutor.RunState run = runs.get(i); + if (!run.running.get()) continue; + if (run.waitingForChat.get()) chatWork = true; + if (run.activeMineTracker != null) mineWork = true; + if (run.waitingForPacket.get()) packetWork = true; + } + + if (s2c && (chatWork || MacroExecutor.isWaitingForChat.get())) { + MacroExecutor.ChatCapture capture = MacroChatObserve.extractIncomingChat(packet); + if (capture != null && capture.message() != null && !capture.message().isBlank()) { + + for (MacroExecutor.RunState run : runs) { + var matched = run.running.get() && run.waitingForChat.get() + ? MacroChatObserve.captureChatAction(run.waitingChatAction, capture, run.variables) + : java.util.Optional.>empty(); + if (matched.isPresent()) { + run.stagedCaptures.put(run.waitingChatAction, matched.get()); + CompletableFuture f = run.chatFuture; + if (f != null) f.complete(null); + } + } + if (MacroExecutor.isCurrentRunActive() && MacroExecutor.isWaitingForChat.get() && MacroChatObserve.matchesWaitingChat(capture)) { + MacroExecutor.lastMatchedChat.set(capture.displayText()); + CompletableFuture f = MacroExecutor.activeChatFuture; + if (f != null) f.complete(null); + } + } + } + + if (s2c && mineWork) { + Minecraft mc = Minecraft.getInstance(); + for (MacroExecutor.RunState run : runs) { + MacroWorldActions.MineProgressTracker tracker = run.running.get() ? run.activeMineTracker : null; + if (tracker != null) tracker.observePacket(mc, packet); + } + } + + if (!MacroExecutor.isCurrentRunActive()) return; + + if (packetWork) { + for (MacroExecutor.RunState run : runs) { + if (!run.running.get() || !run.waitingForPacket.get()) continue; + CompletableFuture f = null; + synchronized (run.packetWaitLock) { + if (run.waitingForPacket.get() && MacroPacketWait.matchesPacketTarget(run.waitingPacketName.get(), packet, direction)) { + run.lastMatchedPacket = packet; + run.lastMatchedPacketDirection = direction; + f = run.packetFuture; + } + } + if (f != null) f.complete(null); + } + } + + if (MacroExecutor.isWaitingForPacket.get()) { + CompletableFuture f = null; + synchronized (MacroExecutor.packetWaitLock) { + if (!MacroExecutor.isWaitingForPacket.get()) return; + + String target = MacroExecutor.waitingPacketName.get(); + if (MacroPacketWait.matchesPacketTarget(target, packet, direction)) { + MacroExecutor.lastReceivedPacket.set(direction + " " + AutismPacketNamer.getFriendlyName(packet, direction)); + f = MacroExecutor.activePacketFuture; + } + } + if (f != null) f.complete(null); + } + } +} diff --git a/src/main/java/autismclient/util/macro/MacroRace.java b/src/main/java/autismclient/util/macro/MacroRace.java index e4b3e6c..56af6f5 100644 --- a/src/main/java/autismclient/util/macro/MacroRace.java +++ b/src/main/java/autismclient/util/macro/MacroRace.java @@ -517,21 +517,21 @@ final class MacroRace { private static CompletableFuture startActionAnchorFuture(MacroAction action) { if (action instanceof PacketClickAction) { - return MacroExecutor.awaitTimedSend(packet -> packet instanceof ServerboundContainerClickPacket).future; + return MacroPacketObserve.awaitTimedSend(packet -> packet instanceof ServerboundContainerClickPacket).future; } if (action instanceof ItemAction) { - return MacroExecutor.awaitTimedSend(packet -> packet instanceof ServerboundContainerClickPacket).future; + return MacroPacketObserve.awaitTimedSend(packet -> packet instanceof ServerboundContainerClickPacket).future; } if (action instanceof CloseGuiAction) { - return MacroExecutor.awaitTimedSend(packet -> packet instanceof net.minecraft.network.protocol.game.ServerboundContainerClosePacket).future; + return MacroPacketObserve.awaitTimedSend(packet -> packet instanceof net.minecraft.network.protocol.game.ServerboundContainerClosePacket).future; } if (action instanceof SendChatAction) { - return MacroExecutor.awaitTimedSend(packet -> packet instanceof net.minecraft.network.protocol.game.ServerboundChatPacket + return MacroPacketObserve.awaitTimedSend(packet -> packet instanceof net.minecraft.network.protocol.game.ServerboundChatPacket || packet instanceof net.minecraft.network.protocol.game.ServerboundChatCommandPacket || packet instanceof net.minecraft.network.protocol.game.ServerboundChatCommandSignedPacket).future; } if (action instanceof SendPacketAction || action instanceof PayloadAction) { - return MacroExecutor.awaitTimedSend(packet -> true).future; + return MacroPacketObserve.awaitTimedSend(packet -> true).future; } return null; } @@ -597,7 +597,7 @@ final class MacroRace { MacroVariableContext context = MacroExecutor.currentVariableContext(); java.util.concurrent.atomic.AtomicReference> captures = new java.util.concurrent.atomic.AtomicReference<>(java.util.Map.of()); - MacroExecutor.TimedOneShotPacketListener listener = MacroExecutor.awaitTimedReceive((packet, direction, now) -> { + MacroPacketObserve.TimedOneShotPacketListener listener = MacroPacketObserve.awaitTimedReceive((packet, direction, now) -> { MacroExecutor.ChatCapture capture = MacroChatObserve.extractIncomingChat(packet); var matched = MacroChatObserve.captureChatAction(chat, capture, context); matched.ifPresent(captures::set); @@ -616,14 +616,14 @@ final class MacroRace { if (trigger instanceof WaitForPacketAction packetAction) { java.util.List targets = packetAction.effectiveList(); CompletableFuture matched = new CompletableFuture<>(); - MacroExecutor.TimedOneShotPacketListener recv = MacroExecutor.awaitTimedReceive((packet, direction, now) -> { + MacroPacketObserve.TimedOneShotPacketListener recv = MacroPacketObserve.awaitTimedReceive((packet, direction, now) -> { if (targets.isEmpty()) return true; for (String target : targets) { if (MacroPacketWait.matchesPacketTarget(target, packet, "S2C")) return true; } return false; }); - MacroExecutor.TimedOneShotPacketListener send = MacroExecutor.awaitTimedSend((packet, direction, now) -> { + MacroPacketObserve.TimedOneShotPacketListener send = MacroPacketObserve.awaitTimedSend((packet, direction, now) -> { if (targets.isEmpty()) return true; for (String target : targets) { if (MacroPacketWait.matchesPacketTarget(target, packet, "C2S")) return true; @@ -641,7 +641,7 @@ final class MacroRace { if (trigger instanceof WaitForWorldChangeAction world) { CompletableFuture done = new CompletableFuture<>(); CompletableFuture tickFuture = MacroConditionRegistry.waitForWorldChange(world); - MacroExecutor.TimedOneShotPacketListener worldListener = MacroExecutor.awaitTimedReceive((packet, direction, now) -> isWorldTransitionPacket(packet)); + MacroPacketObserve.TimedOneShotPacketListener worldListener = MacroPacketObserve.awaitTimedReceive((packet, direction, now) -> isWorldTransitionPacket(packet)); tickFuture.whenComplete((v, e) -> { if (e != null) { if (!done.isDone()) done.cancel(true); @@ -667,7 +667,7 @@ final class MacroRace { if (trigger instanceof WaitForTeleportAction teleport) { net.minecraft.world.phys.Vec3 origin = mc.player == null ? net.minecraft.world.phys.Vec3.ZERO : mc.player.position(); CompletableFuture done = new CompletableFuture<>(); - MacroExecutor.TimedOneShotPacketListener teleListener = MacroExecutor.awaitTimedReceive((packet, direction, now) -> + MacroPacketObserve.TimedOneShotPacketListener teleListener = MacroPacketObserve.awaitTimedReceive((packet, direction, now) -> packet instanceof ClientboundPlayerPositionPacket || isWorldTransitionPacket(packet)); teleListener.future.whenComplete((event, error) -> { teleListener.cancel();