mirror of
https://github.com/AutismDevelopment/Autism-Client.git
synced 2026-10-28 19:39:48 +00:00
Refactor: extract MacroPrearm from MacroExecutor (upstream #4)
Move 24 prearm/capture-commit/MacroValue-builder methods verbatim into MacroPrearm (predicateFuture through waitForPacketMatch). Shared members referenced as MacroExecutor.xxx (mechanical qualification only); staying and earlier-helper call sites re-pointed to MacroPrearm.xxx. PrearmedWait widened to package-visible. All bodies verified identical against HEAD modulo that qualification.
This commit is contained in:
parent
f4fcb7bca9
commit
f014d6e5c0
4 changed files with 587 additions and 565 deletions
518
src/main/java/autismclient/util/macro/MacroPrearm.java
Normal file
518
src/main/java/autismclient/util/macro/MacroPrearm.java
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
package autismclient.util.macro;
|
||||
|
||||
import autismclient.util.AutismInventoryHelper;
|
||||
import autismclient.util.AutismPacketNamer;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
final class MacroPrearm {
|
||||
private MacroPrearm() {
|
||||
}
|
||||
public static CompletableFuture<Void> predicateFuture(java.util.function.BooleanSupplier predicate, int timeoutMs) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
Thread worker = new Thread(() -> MacroExecutor.withRunContext(run, () -> {
|
||||
long start = System.currentTimeMillis();
|
||||
while (!future.isDone() && MacroExecutor.isCurrentRunActive()) {
|
||||
try {
|
||||
if (predicate.getAsBoolean()) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
} catch (Throwable ignored) { }
|
||||
if (timeoutMs > 0 && System.currentTimeMillis() - start >= timeoutMs) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
java.util.concurrent.locks.LockSupport.parkNanos(MacroExecutor.MACRO_WAIT_POLL_NANOS);
|
||||
if (Thread.interrupted()) return;
|
||||
}
|
||||
}), "MacroPredicateWait");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
future.whenComplete((v, e) -> worker.interrupt());
|
||||
return future;
|
||||
}
|
||||
|
||||
public static void prearmNextActionWait(Minecraft mc, java.util.List<MacroAction> actions, int currentIndex) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (run == null || actions == null) return;
|
||||
int nextIndex = currentIndex + 1;
|
||||
if (nextIndex < 0 || nextIndex >= actions.size()) return;
|
||||
MacroAction next = actions.get(nextIndex);
|
||||
if (next == null || !next.isEnabled() || !next.listensDuringPreviousAction()) return;
|
||||
if (!MacroDynamicBindings.apply(next, mc)) return;
|
||||
if (!MacroExecutor.dynamicTextInputsAvailable(next, mc)) return;
|
||||
if (!isWaitConditionAction(next)) return;
|
||||
synchronized (run.prearmedWaits) {
|
||||
if (run.prearmedWaits.containsKey(next)) return;
|
||||
clearCaptureOutputs(next);
|
||||
clearOwnCaptureOutput(next);
|
||||
MacroExecutor.PrearmedWait wait = createPrearmedWait(mc, next);
|
||||
if (wait != null) run.prearmedWaits.put(next, wait);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWaitConditionAction(MacroAction action) {
|
||||
return action instanceof WaitForPacketAction
|
||||
|| action instanceof WaitPacketMatchAction
|
||||
|| action instanceof WaitForChatAction
|
||||
|| action instanceof WaitForGuiAction
|
||||
|| action instanceof WaitGuiTypeAction
|
||||
|| action instanceof WaitForSoundAction
|
||||
|| action instanceof WaitForSlotChangeAction
|
||||
|| action instanceof WaitInventoryPredicateAction
|
||||
|| action instanceof WaitDurabilityAction
|
||||
|| action instanceof WaitFreeSlotsAction
|
||||
|| action instanceof WaitForHealthAction
|
||||
|| action instanceof WaitForBlockAction
|
||||
|| action instanceof WaitForEntityAction
|
||||
|| action instanceof WaitEntityTargetAction
|
||||
|| action instanceof WaitForCooldownAction
|
||||
|| action instanceof WaitPosAction
|
||||
|| action instanceof WaitForPositionDeltaAction
|
||||
|| action instanceof WaitForTeleportAction
|
||||
|| action instanceof WaitGamemodeChangeAction
|
||||
|| action instanceof WaitForWorldChangeAction
|
||||
|| action instanceof WaitMovementAction
|
||||
|| action instanceof WaitForLanStepAction
|
||||
|| action instanceof WaitForMacroStepAction;
|
||||
}
|
||||
|
||||
public static MacroExecutor.PrearmedWait consumePrearmedWait(MacroAction action) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (run == null || action == null) return null;
|
||||
synchronized (run.prearmedWaits) {
|
||||
return run.prearmedWaits.remove(action);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean waitForPrearmed(MacroAction action, long timeoutMs) {
|
||||
MacroExecutor.PrearmedWait wait = consumePrearmedWait(action);
|
||||
if (wait == null) return false;
|
||||
try {
|
||||
MacroExecutor.waitForFutureDone(wait.future, timeoutMs);
|
||||
commitStagedCapture(action);
|
||||
return true;
|
||||
} finally {
|
||||
wait.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private static MacroExecutor.PrearmedWait createPrearmedWait(Minecraft mc, MacroAction action) {
|
||||
if (action instanceof WaitForPacketAction packetAction) {
|
||||
java.util.List<String> targets = packetAction.effectiveList();
|
||||
String firstTarget = targets.isEmpty() ? "" : targets.get(0);
|
||||
return packetTargetFuture(packetAction, firstTarget, 1);
|
||||
}
|
||||
if (action instanceof WaitPacketMatchAction packetMatch) {
|
||||
return packetMatchFuture(packetMatch);
|
||||
}
|
||||
if (action instanceof WaitInventoryPredicateAction inv) {
|
||||
inv.resetBaseline();
|
||||
return predicatePrearm(action, () -> inv.matches(mc), inv.timeoutMs);
|
||||
}
|
||||
if (action instanceof WaitDurabilityAction durability) {
|
||||
if (durability.useNext) return null;
|
||||
return predicatePrearm(action, () -> MacroDurability.durabilityObservationMatches(mc, durability), durability.timeoutMs);
|
||||
}
|
||||
if (action instanceof WaitFreeSlotsAction freeSlots) {
|
||||
return predicatePrearm(action, () -> freeSlots.matches(mc), freeSlots.timeoutMs);
|
||||
}
|
||||
if (action instanceof WaitEntityTargetAction entityTarget) {
|
||||
return predicatePrearm(action, () -> entityTarget.matches(mc), entityTarget.timeoutMs);
|
||||
}
|
||||
if (action instanceof WaitGuiTypeAction guiType) {
|
||||
return predicatePrearm(action,
|
||||
guiType.waitMode == WaitGuiTypeAction.WaitMode.CLOSE
|
||||
? MacroExecutor.guiClosePredicate(guiType, mc)
|
||||
: () -> {
|
||||
var capture = guiType.capture(mc);
|
||||
capture.ifPresent(values -> stageCapture(guiType, values));
|
||||
return capture.isPresent();
|
||||
},
|
||||
guiType.timeoutMs);
|
||||
}
|
||||
if (action instanceof WaitForChatAction chat) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
MacroExecutor.OneShotPacketListener listener = MacroExecutor.awaitReceive(packet -> {
|
||||
MacroExecutor.ChatCapture capture = MacroChatObserve.extractIncomingChat(packet);
|
||||
var matched = MacroChatObserve.captureChatAction(chat, capture, run == null ? null : run.variables);
|
||||
if (matched.isEmpty()) return false;
|
||||
if (run != null) run.stagedCaptures.put(chat, matched.get());
|
||||
return true;
|
||||
});
|
||||
CompletableFuture<?> future = listener.future;
|
||||
if (chat.timeoutMs > 0) future.completeOnTimeout(null, chat.timeoutMs, TimeUnit.MILLISECONDS);
|
||||
return new MacroExecutor.PrearmedWait(action, future, listener::cancel, 0);
|
||||
}
|
||||
if (action instanceof WaitForMacroStepAction macroStep) {
|
||||
return predicatePrearm(action, () -> MacroExecutor.isMacroStepSatisfied(macroStep), macroStep.timeoutMs);
|
||||
}
|
||||
if (action instanceof WaitForLanStepAction lanStep) {
|
||||
CompletableFuture<Void> future = MacroConditionRegistry.waitForLanStep(lanStep);
|
||||
return new MacroExecutor.PrearmedWait(action, future, () -> {
|
||||
if (!future.isDone()) future.cancel(true);
|
||||
}, 0);
|
||||
}
|
||||
CompletableFuture<?> future = MacroExecutor.raceConditionFuture(mc, action);
|
||||
if (future == null) return null;
|
||||
int timeout = action instanceof WaitForChatAction chat ? chat.timeoutMs : 0;
|
||||
if (timeout > 0) future.completeOnTimeout(null, timeout, TimeUnit.MILLISECONDS);
|
||||
return new MacroExecutor.PrearmedWait(action, future, () -> {
|
||||
if (!future.isDone()) future.cancel(true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public static boolean hasPrearmedWait(MacroAction action) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (run == null || action == null) return false;
|
||||
synchronized (run.prearmedWaits) {
|
||||
return run.prearmedWaits.containsKey(action);
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordMatchedPacket(MacroAction action, Packet<?> packet, String direction) {
|
||||
if (action instanceof WaitForPacketAction wait) {
|
||||
wait.matchedPacket = packet;
|
||||
wait.matchedDirection = direction == null ? "" : direction;
|
||||
} else if (action instanceof WaitPacketMatchAction wait) {
|
||||
wait.matchedPacket = packet;
|
||||
wait.matchedDirection = direction == null ? "" : direction;
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyLastMatchedPacket(WaitForPacketAction action) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (action == null || run == null || run.lastMatchedPacket == null) return;
|
||||
action.matchedPacket = run.lastMatchedPacket;
|
||||
action.matchedDirection = run.lastMatchedPacketDirection;
|
||||
}
|
||||
|
||||
static void clearCaptureOutputs(MacroAction action) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (run == null || action == null) return;
|
||||
run.stagedCaptures.remove(action);
|
||||
if (action instanceof WaitForChatAction chat) {
|
||||
for (String name : MacroCapturePattern.declaredNames(chat.effectiveMatchMode(), chat.pattern)) run.variables.remove(name);
|
||||
if (chat.saveAs != null && !chat.saveAs.isBlank()) run.variables.remove(chat.saveAs);
|
||||
} else if (action instanceof WaitForGuiAction gui) {
|
||||
for (String name : MacroCapturePattern.declaredNames(gui.matchMode, gui.guiTitle)) run.variables.remove(name);
|
||||
if (gui.saveAs != null && !gui.saveAs.isBlank()) run.variables.remove(gui.saveAs);
|
||||
} else if (action instanceof WaitGuiTypeAction gui) {
|
||||
for (String name : MacroCapturePattern.declaredNames(gui.matchMode, gui.title)) run.variables.remove(name);
|
||||
if (gui.saveAs != null && !gui.saveAs.isBlank()) run.variables.remove(gui.saveAs);
|
||||
} else if (action instanceof CaptureValueAction capture) {
|
||||
for (String name : MacroCapturePattern.declaredNames(capture.matchMode, capture.pattern)) run.variables.remove(name);
|
||||
if (capture.saveAs != null && !capture.saveAs.isBlank()) run.variables.remove(capture.saveAs);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearOwnCaptureOutput(MacroAction action) {
|
||||
if (!(action instanceof MacroCaptureOutput output)) return;
|
||||
String name = output.getSaveAs();
|
||||
if (name != null && !name.isBlank()) MacroVariables.remove(name);
|
||||
if (action instanceof WaitForSoundAction sound) sound.matchedSoundId = "";
|
||||
if (action instanceof WaitForPacketAction packet) {
|
||||
packet.matchedPacket = null;
|
||||
packet.matchedDirection = "";
|
||||
}
|
||||
if (action instanceof WaitPacketMatchAction packet) {
|
||||
packet.matchedPacket = null;
|
||||
packet.matchedDirection = "";
|
||||
}
|
||||
}
|
||||
|
||||
public static void commitConditionOutput(MacroAction action, Minecraft mc) throws InterruptedException {
|
||||
if (!(action instanceof MacroCaptureOutput output)) return;
|
||||
String name = MacroVariableContext.cleanRootName(output.getSaveAs());
|
||||
if (name.isEmpty()) return;
|
||||
MacroValue value = MacroExecutor.callOnClientThread(mc, () -> conditionOutputValue(action, mc), null);
|
||||
if (value != null) MacroVariables.setValue(name, value);
|
||||
}
|
||||
|
||||
public static MacroValue conditionOutputValue(MacroAction action, Minecraft mc) {
|
||||
if (action instanceof WaitForHealthAction healthAction && mc != null && mc.player != null) {
|
||||
float health = mc.player.getHealth();
|
||||
if (healthAction.below ? health >= healthAction.healthThreshold : health <= healthAction.healthThreshold) return null;
|
||||
return MacroValue.structured(MacroValue.Kind.NUMBER, Float.toString(health), java.util.Map.of(
|
||||
"health", MacroValue.number(health),
|
||||
"max", MacroValue.number(mc.player.getMaxHealth()),
|
||||
"absorption", MacroValue.number(mc.player.getAbsorptionAmount())
|
||||
));
|
||||
}
|
||||
if (action instanceof WaitForEntityAction entityAction) {
|
||||
net.minecraft.world.entity.Entity entity = entityAction.findMatchingEntity(mc);
|
||||
if (entity == null) return null;
|
||||
String id = BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
|
||||
double distance = mc != null && mc.player != null ? mc.player.distanceTo(entity) : 0.0;
|
||||
return MacroValue.structured(MacroValue.Kind.ENTITY, entity.getName().getString(), java.util.Map.of(
|
||||
"name", MacroValue.text(entity.getName().getString()),
|
||||
"id", MacroValue.identifier(id),
|
||||
"uuid", MacroValue.text(entity.getStringUUID()),
|
||||
"x", MacroValue.number(entity.getX()),
|
||||
"y", MacroValue.number(entity.getY()),
|
||||
"z", MacroValue.number(entity.getZ()),
|
||||
"distance", MacroValue.number(distance)
|
||||
));
|
||||
}
|
||||
if (action instanceof WaitForSoundAction sound) {
|
||||
if (sound.matchedSoundId == null || sound.matchedSoundId.isBlank()) return null;
|
||||
return MacroValue.structured(MacroValue.Kind.IDENTIFIER, sound.matchedSoundId, java.util.Map.of(
|
||||
"id", MacroValue.identifier(sound.matchedSoundId),
|
||||
"x", MacroValue.number(sound.matchedX),
|
||||
"y", MacroValue.number(sound.matchedY),
|
||||
"z", MacroValue.number(sound.matchedZ)
|
||||
));
|
||||
}
|
||||
if (action instanceof WaitForPacketAction packet) {
|
||||
return packetMacroValue(packet.matchedPacket, packet.matchedDirection);
|
||||
}
|
||||
if (action instanceof WaitPacketMatchAction packet) {
|
||||
return packetMacroValue(packet.matchedPacket, packet.matchedDirection);
|
||||
}
|
||||
if (action instanceof WaitInventoryPredicateAction inventory) {
|
||||
if (!inventory.matches(mc)) return null;
|
||||
return inventoryMacroValue(mc, inventory.itemName, inventory.slot, inventory.condition.name());
|
||||
}
|
||||
if (action instanceof WaitForSlotChangeAction slotWait) {
|
||||
if (slotWait.entries.isEmpty()) return inventoryMacroValue(mc, "", -1, "slot_change");
|
||||
ItemTarget target = slotWait.entries.get(0).resolvedTarget().resolveTemplate(mc);
|
||||
return target == null ? null : itemTargetMacroValue(mc, target);
|
||||
}
|
||||
if (action instanceof WaitPosAction || action instanceof WaitForTeleportAction
|
||||
|| action instanceof WaitForPositionDeltaAction) {
|
||||
return playerPositionMacroValue(mc);
|
||||
}
|
||||
if (action instanceof WaitForWorldChangeAction && mc != null && mc.level != null) {
|
||||
String dimension = mc.level.dimension().identifier().toString();
|
||||
return MacroValue.structured(MacroValue.Kind.IDENTIFIER, dimension, java.util.Map.of(
|
||||
"id", MacroValue.identifier(dimension), "name", MacroValue.text(dimension)));
|
||||
}
|
||||
if (action instanceof WaitGamemodeChangeAction && mc != null && mc.gameMode != null) {
|
||||
String mode = mc.gameMode.getPlayerMode().getName();
|
||||
WaitGamemodeChangeAction wait = (WaitGamemodeChangeAction) action;
|
||||
if (wait.match == WaitGamemodeChangeAction.Match.TO_MODE && !wait.accepts(mc.gameMode.getPlayerMode())) return null;
|
||||
return MacroValue.structured(MacroValue.Kind.TEXT, mode, java.util.Map.of("name", MacroValue.text(mode)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MacroValue playerPositionMacroValue(Minecraft mc) {
|
||||
if (mc == null || mc.player == null) return null;
|
||||
String display = String.format(Locale.ROOT, "%.3f, %.3f, %.3f", mc.player.getX(), mc.player.getY(), mc.player.getZ());
|
||||
return MacroValue.structured(MacroValue.Kind.POSITION, display, java.util.Map.of(
|
||||
"x", MacroValue.number(mc.player.getX()), "y", MacroValue.number(mc.player.getY()),
|
||||
"z", MacroValue.number(mc.player.getZ()), "yaw", MacroValue.number(mc.player.getYRot()),
|
||||
"pitch", MacroValue.number(mc.player.getXRot())
|
||||
));
|
||||
}
|
||||
|
||||
private static MacroValue inventoryMacroValue(Minecraft mc, String itemTemplate, int slot, String condition) {
|
||||
if (mc == null || mc.player == null) return null;
|
||||
MacroTemplate.Resolution item = MacroVariables.resolve(itemTemplate, mc);
|
||||
if (!item.success()) return null;
|
||||
ItemTarget target = ItemTarget.fromLegacyEntry(item.value());
|
||||
if (slot >= 0) {
|
||||
int safeSlot = Math.max(0, Math.min(mc.player.getInventory().getContainerSize() - 1, slot));
|
||||
MacroValue stack = stackMacroValue(mc.player.getInventory().getItem(safeSlot), safeSlot);
|
||||
return stack == null ? MacroValue.structured(MacroValue.Kind.SLOT, Integer.toString(slot), java.util.Map.of(
|
||||
"slot", MacroValue.slot(slot), "empty", MacroValue.text("true"), "condition", MacroValue.text(condition))) : stack;
|
||||
}
|
||||
if (target.hasIdentity()) {
|
||||
MacroValue matched = itemTargetMacroValue(mc, target);
|
||||
if (matched != null) return matched.withProperty("condition", MacroValue.text(condition));
|
||||
}
|
||||
return MacroValue.structured(MacroValue.Kind.TEXT, condition, java.util.Map.of(
|
||||
"condition", MacroValue.text(condition),
|
||||
"selected_slot", MacroValue.slot(mc.player.getInventory().getSelectedSlot())
|
||||
));
|
||||
}
|
||||
|
||||
private static MacroValue itemTargetMacroValue(Minecraft mc, ItemTarget target) {
|
||||
if (mc == null || mc.player == null || target == null) return null;
|
||||
if (mc.player.containerMenu != null) {
|
||||
for (Slot slot : mc.player.containerMenu.slots) {
|
||||
if (slot == null || slot.getItem().isEmpty()) continue;
|
||||
int visible = AutismInventoryHelper.toUserVisibleSlot(mc, slot.index);
|
||||
if (target.score(slot.getItem(), visible) >= 0) return stackMacroValue(slot.getItem(), visible);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MacroValue stackMacroValue(ItemStack stack, int slot) {
|
||||
if (stack == null || stack.isEmpty()) return null;
|
||||
Identifier id = BuiltInRegistries.ITEM.getKey(stack.getItem());
|
||||
String itemId = id == null ? "" : id.toString();
|
||||
String name = stack.getHoverName().getString();
|
||||
return MacroValue.structured(MacroValue.Kind.ITEM, name, java.util.Map.of(
|
||||
"name", MacroValue.text(name), "id", MacroValue.identifier(itemId),
|
||||
"count", MacroValue.number(stack.getCount()), "slot", MacroValue.slot(slot)
|
||||
));
|
||||
}
|
||||
|
||||
private static MacroValue packetMacroValue(Packet<?> packet, String direction) {
|
||||
if (packet == null) return null;
|
||||
java.util.LinkedHashMap<String, MacroValue> properties = new java.util.LinkedHashMap<>();
|
||||
String packetName = AutismPacketNamer.getFriendlyName(packet, direction);
|
||||
properties.put("name", MacroValue.text(packetName));
|
||||
properties.put("class", MacroValue.text(packet.getClass().getName()));
|
||||
properties.put("direction", MacroValue.text(direction));
|
||||
RecordComponent[] components = packet.getClass().getRecordComponents();
|
||||
if (components != null) {
|
||||
for (RecordComponent component : components) {
|
||||
if (properties.size() >= 64) break;
|
||||
try {
|
||||
Object value = component.getAccessor().invoke(packet);
|
||||
properties.put(component.getName().toLowerCase(Locale.ROOT), MacroValue.text(value));
|
||||
} catch (ReflectiveOperationException ignored) { }
|
||||
}
|
||||
}
|
||||
return MacroValue.structured(MacroValue.Kind.PACKET, packetName, properties);
|
||||
}
|
||||
|
||||
public static void stageCapture(MacroAction action, java.util.Map<String, MacroValue> values) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (run == null || action == null || values == null) return;
|
||||
run.stagedCaptures.put(action, java.util.Map.copyOf(values));
|
||||
}
|
||||
|
||||
static void commitStagedCapture(MacroAction action) {
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
if (run == null || action == null) return;
|
||||
java.util.Map<String, MacroValue> values = run.stagedCaptures.remove(action);
|
||||
if (values != null) run.variables.setAll(values);
|
||||
}
|
||||
|
||||
private static MacroExecutor.PrearmedWait predicatePrearm(MacroAction action, java.util.function.BooleanSupplier predicate, int timeoutMs) {
|
||||
java.util.concurrent.atomic.AtomicBoolean active = new java.util.concurrent.atomic.AtomicBoolean(true);
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
MacroExecutor.RunState run = MacroExecutor.CURRENT_RUN.get();
|
||||
Thread worker = new Thread(() -> MacroExecutor.withRunContext(run, () -> {
|
||||
long start = System.currentTimeMillis();
|
||||
while (active.get() && MacroExecutor.isCurrentRunActive() && !future.isDone()) {
|
||||
try {
|
||||
if (predicate.getAsBoolean()) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
} catch (Throwable ignored) { }
|
||||
if (timeoutMs > 0 && System.currentTimeMillis() - start >= timeoutMs) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
java.util.concurrent.locks.LockSupport.parkNanos(MacroExecutor.MACRO_WAIT_POLL_NANOS);
|
||||
if (Thread.interrupted()) return;
|
||||
}
|
||||
}), "MacroPrearmedWait");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
return new MacroExecutor.PrearmedWait(action, future, () -> {
|
||||
active.set(false);
|
||||
if (!future.isDone()) future.cancel(true);
|
||||
worker.interrupt();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private static MacroExecutor.PrearmedWait packetTargetFuture(MacroAction action, String target, int completedPacketTargets) {
|
||||
String normalizedTarget = WaitForPacketAction.normalizeTarget(target);
|
||||
String direction = WaitForPacketAction.getDirection(normalizedTarget);
|
||||
CompletableFuture<Packet<?>> matched = new CompletableFuture<>();
|
||||
MacroExecutor.OneShotPacketListener send = null;
|
||||
MacroExecutor.OneShotPacketListener recv = null;
|
||||
if (!"S2C".equalsIgnoreCase(direction)) {
|
||||
send = MacroExecutor.awaitSend(packet -> MacroPacketWait.matchesPacketTarget(normalizedTarget, packet, "C2S"));
|
||||
send.future.thenAccept(packet -> {
|
||||
recordMatchedPacket(action, packet, "C2S");
|
||||
matched.complete(packet);
|
||||
});
|
||||
}
|
||||
if (!"C2S".equalsIgnoreCase(direction)) {
|
||||
recv = MacroExecutor.awaitReceive(packet -> MacroPacketWait.matchesPacketTarget(normalizedTarget, packet, "S2C"));
|
||||
recv.future.thenAccept(packet -> {
|
||||
recordMatchedPacket(action, packet, "S2C");
|
||||
matched.complete(packet);
|
||||
});
|
||||
}
|
||||
MacroExecutor.OneShotPacketListener finalSend = send;
|
||||
MacroExecutor.OneShotPacketListener finalRecv = recv;
|
||||
return new MacroExecutor.PrearmedWait(action, matched, () -> {
|
||||
if (finalSend != null) finalSend.cancel();
|
||||
if (finalRecv != null) finalRecv.cancel();
|
||||
if (!matched.isDone()) matched.cancel(true);
|
||||
}, completedPacketTargets);
|
||||
}
|
||||
|
||||
private static MacroExecutor.PrearmedWait packetMatchFuture(WaitPacketMatchAction action) {
|
||||
MacroExecutor.OneShotPacketListener send = null;
|
||||
MacroExecutor.OneShotPacketListener recv = null;
|
||||
CompletableFuture<Packet<?>> matched = new CompletableFuture<>();
|
||||
if (action.direction != WaitPacketMatchAction.Direction.S2C) {
|
||||
send = MacroExecutor.awaitSend(packet -> action.matches(packet, "C2S"));
|
||||
send.future.thenAccept(packet -> {
|
||||
recordMatchedPacket(action, packet, "C2S");
|
||||
matched.complete(packet);
|
||||
});
|
||||
}
|
||||
if (action.direction != WaitPacketMatchAction.Direction.C2S) {
|
||||
recv = MacroExecutor.awaitReceive(packet -> action.matches(packet, "S2C"));
|
||||
recv.future.thenAccept(packet -> {
|
||||
recordMatchedPacket(action, packet, "S2C");
|
||||
matched.complete(packet);
|
||||
});
|
||||
}
|
||||
if (send == null && recv == null) return null;
|
||||
if (action.timeoutMs > 0) matched.completeOnTimeout(null, action.timeoutMs, TimeUnit.MILLISECONDS);
|
||||
MacroExecutor.OneShotPacketListener finalSend = send;
|
||||
MacroExecutor.OneShotPacketListener finalRecv = recv;
|
||||
return new MacroExecutor.PrearmedWait(action, matched, () -> {
|
||||
if (finalSend != null) finalSend.cancel();
|
||||
if (finalRecv != null) finalRecv.cancel();
|
||||
if (!matched.isDone()) matched.cancel(true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public static void waitForPacketMatch(WaitPacketMatchAction action) {
|
||||
if (action == null) return;
|
||||
MacroExecutor.OneShotPacketListener send = null;
|
||||
MacroExecutor.OneShotPacketListener recv = null;
|
||||
try {
|
||||
if (action.direction != WaitPacketMatchAction.Direction.S2C) {
|
||||
send = MacroExecutor.awaitSend(packet -> {
|
||||
boolean matches = action.matches(packet, "C2S");
|
||||
if (matches) recordMatchedPacket(action, packet, "C2S");
|
||||
return matches;
|
||||
});
|
||||
}
|
||||
if (action.direction != WaitPacketMatchAction.Direction.C2S) {
|
||||
recv = MacroExecutor.awaitReceive(packet -> {
|
||||
boolean matches = action.matches(packet, "S2C");
|
||||
if (matches) recordMatchedPacket(action, packet, "S2C");
|
||||
return matches;
|
||||
});
|
||||
}
|
||||
CompletableFuture<?> future;
|
||||
if (send != null && recv != null) future = CompletableFuture.anyOf(send.future, recv.future);
|
||||
else if (send != null) future = send.future;
|
||||
else if (recv != null) future = recv.future;
|
||||
else return;
|
||||
if (action.timeoutMs > 0) future.completeOnTimeout(null, action.timeoutMs, TimeUnit.MILLISECONDS);
|
||||
MacroExecutor.waitForFutureDone(future, action.timeoutMs);
|
||||
} finally {
|
||||
if (send != null) send.cancel();
|
||||
if (recv != null) recv.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue