Guías
LDPlayer macro loop: bucles, parada y programación
Monta un LDPlayer macro loop o uno en BlueStacks con condiciones de parada, guarda el progreso entre ejecuciones, sal con un código y prográmalo a diario.
- Windows
- Mac
- Emulador
- Studio
- SDK de Python
En esta página
- Antes de empezar
- Patrones de LDPlayer macro loop y de BlueStacks
- Condiciones de parada de un macro loop
- Pausas y tiempos humanizados
- Guardar el progreso con el almacenamiento
- Salir con un código para que los webhooks informen del estado
- Un ejemplo completo
- Programar la macro en el Programador de MAS en BlueStacks o LDPlayer
- Ejecutarla en un grupo
- Qué puede salir mal
- El bucle nunca termina
- La programación no se disparó
- Time slot is occupied on this port
- La ejecución aparece como Fallida pero la macro terminó
Una macro que se ejecuta sin vigilancia necesita tres cosas: un bucle que sepa cuándo parar, una forma de recordar hasta dónde llegó y un horario que la arranque sin ti. Esta guía cubre las tres para un LDPlayer macro loop, o uno en BlueStacks, en Macro Automation Studio (MAS). Tienes los patrones de bucle, las condiciones de parada, el almacenamiento entre ejecuciones, los códigos de salida que los webhooks pueden notificar y el Programador. Está pensada para quien lleva una macro de “hago clic en Ejecutar” a “se ejecuta todas las mañanas”.
Antes de empezar
- MAS está instalado y con sesión iniciada. Es una descarga gratuita para la prueba.
- Un proyecto basado en código se ejecuta en un emulador desde el Code Editor. Consulta Primeros pasos.
- El emulador está añadido como dispositivo con un puerto que puedes ver en su tarjeta. Consulta Dispositivos.
- Tus plantillas y regiones están capturadas. Consulta la guía de reconocimiento de imagen.
Patrones de LDPlayer macro loop y de BlueStacks
Tres formas cubren casi todas las macros.
While, con una condición de parada. El bucle se ejecuta hasta que algo en pantalla o en tus contadores dice que pare.
import mas
images = mas.images({"collect": 401, "done": 402})
collected = 0
while collected < 50:
if mas.find_object(images.done):
break
button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
if button is None:
continue
mas.click(button.x, button.y, delay_ms=1000)
collected += 1For con un número fijo. Sabes cuántas veces hay que hacerlo.
for round_no in range(1, 11):
mas.log(f"Round {round_no} of 10")
button = mas.find_object_retry(images.collect)
if button is None:
mas.log("Nothing to collect, stopping early", level="warning")
break
mas.click(button.x, button.y)Hasta que aparezca una plantilla. Una espera con plazo, usada en pantallas de carga y temporizadores largos.
import time
def wait_for(image, timeout_s=120, every_s=3.0):
deadline = time.monotonic() + timeout_s
while time.monotonic() < deadline:
match = mas.find_object(image)
if match:
return match
time.sleep(every_s)
return NoneTodos los bucles de arriba tienen una salida. find_object devuelve None cuando falla y nunca lanza una excepción, así que una plantilla ausente por sí sola no puede terminar un while True. Ponle un límite a cada bucle.
Condiciones de parada de un macro loop
Combina al menos dos. Una te protege del juego y la otra de tu propio error.
- Un contador.
while collected < 50. Barato y predecible. - Un presupuesto de tiempo.
time.monotonic()cuenta segundos y nunca salta cuando cambia el reloj. Úsalo en el bucle exterior para que una ejecución programada termine siempre antes de que empiece la siguiente. - Un estado de pantalla. Una plantilla como “sin energía”, “inventario lleno” o un mensaje de límite diario. Captúrala en Asset Lab como cualquier otra plantilla y compruébala al principio de cada pasada.
- Fallos consecutivos. Cuenta cuántas pasadas seguidas no encontraron nada y ríndete a las cinco. Una pantalla que no habías previsto se ve exactamente así.
started = time.monotonic()
misses = 0
while time.monotonic() - started < 20 * 60:
if mas.find_object(images.out_of_energy):
break
button = mas.find_object_retry(images.collect)
if button is None:
misses += 1
if misses >= 5:
break
continue
misses = 0
mas.click(button.x, button.y)Pausas y tiempos humanizados
click ya espera delay_ms=1000 tras el toque y swipe dura duration_ms=1000 por defecto. Entre pasadas, añade una pausa que varíe.
import random
def pause(low=0.8, high=2.5):
time.sleep(random.uniform(low, high))Los intervalos fijos parecen mecánicos y además compiten con el juego. Un aviso que aparece 900 ms después de un toque se cuela por delante de un retardo fijo de 1000 ms en un día lento. Una pausa aleatoria suaviza ambas cosas. Mantén delay_ms en click para el tiempo de reacción de la app y usa pause() para el ritmo humano.
Guardar el progreso con el almacenamiento
Una ejecución programada que se detiene a medias debería retomar donde lo dejó la próxima vez. Las funciones de almacenamiento guardan un pequeño documento JSON por tarea, indexado por este ordenador, el puerto del dispositivo y un nombre de tarea que eliges tú.
TASK = "daily_collect"
state = mas.retrieve(TASK) # {} on the first run
collected = state.get("collected", 0)
last_day = state.get("day")
mas.save(TASK, {"collected": collected, "day": today}) # after each pass of the loop
if collected >= 50:
mas.clear(TASK) # start fresh next timesave(task_name, data) escribe o sustituye la entrada; retrieve(task_name) devuelve el diccionario o uno vacío; clear(task_name) la borra. data debe ser serializable a JSON. Como el puerto forma parte de la clave, dos instancias de emulador que ejecutan la misma macro nunca comparten un contador. retrieve_all(task_name) devuelve la entrada de cada instancia cuando quieres un total. Consulta Almacenamiento.
Salir con un código para que los webhooks informen del estado
MAS lee el código de salida del proceso cuando tu script termina. Cero significa que la ejecución se completó; cualquier otro valor la marca como fallida. Los suscriptores de webhooks reciben macro.completed o macro.failed según corresponda, con el exit_code en el payload.
import sys
if energy is None:
mas.log("Could not read energy", level="error")
sys.exit(2) # macro.failed, exit_code 2
mas.log("All done")
sys.exit(0) # macro.completedUna excepción no controlada también sale con un valor distinto de cero, así que un fallo se notifica como error sin código adicional. Para eventos que solo tu script conoce, mas.webhook("level.reached", {"level": 40}) envía un evento custom.level.reached a los endpoints suscritos a eventos personalizados. Configura los endpoints en la página Webhooks; consulta Webhooks.
Un ejemplo completo
La macro recoge hasta cincuenta recompensas al día, recuerda su recuento, se detiene con “sin energía” o tras veinte minutos, e informa mediante su código de salida. Sustituye los ID por los tuyos.
import datetime
import random
import sys
import time
import mas
images = mas.images({
"collect": 401,
"close_popup": 402,
"out_of_energy": 403,
})
TASK = "daily_collect"
DAILY_LIMIT = 50
TIME_BUDGET_S = 20 * 60
def pause(low=0.8, high=2.5):
time.sleep(random.uniform(low, high))
def main():
today = datetime.date.today().isoformat()
state = mas.retrieve(TASK)
collected = state.get("collected", 0) if state.get("day") == today else 0
mas.log(f"{today}: starting at {collected} of {DAILY_LIMIT}")
started = time.monotonic()
misses = 0
while collected < DAILY_LIMIT:
if time.monotonic() - started > TIME_BUDGET_S:
mas.log("Time budget reached", level="warning")
break
popup = mas.find_object(images.close_popup)
if popup:
mas.click(popup.x, popup.y, delay_ms=800)
continue
if mas.find_object(images.out_of_energy):
mas.log("Out of energy")
break
button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
if button is None:
misses += 1
if misses >= 5:
mas.log("Five misses in a row, giving up", level="error")
mas.save(TASK, {"collected": collected, "day": today})
sys.exit(2)
continue
misses = 0
mas.click(button.x, button.y, delay_ms=1000)
collected += 1
mas.save(TASK, {"collected": collected, "day": today})
pause()
mas.log(f"Finished at {collected} of {DAILY_LIMIT}")
sys.exit(0)
if __name__ == "__main__":
main()Ejecútala una vez desde el Code Editor con Ejecutar (F5) y confirma que termina sola antes de programarla.
Programar la macro en el Programador de MAS en BlueStacks o LDPlayer
El Programador vive dentro de la app de escritorio y arranca macros en tus emuladores a las horas que fijes.
- Abre Programador y haz clic en Crear nueva programación.
- Escribe un Nombre (de 3 a 100 caracteres) y elige la Macro.
- En Puerto del emulador, haz clic en Escanear y elige el puerto de la instancia, o escríbelo.
- Fija la Fecha y la Hora. La hora es la hora local de tu ordenador.
- Fija la Recurrencia: Ninguna para una sola ejecución, Diaria, Semanal con los Días de la semana marcados, o Mensual.
- Deja el Número de repeticiones en 1 salvo que quieras que la macro se ejecute varias veces seguidas en cada disparo.
- Deja Activa encendida y haz clic en Crear programación.
Lo que debes saber antes de confiar en él:
- La app debe seguir abierta y con sesión iniciada. El programador no es un servicio del sistema y no hay sintaxis cron.
- Las tareas se comprueban cada 30 segundos, así que una ejecución puede empezar hasta medio minuto después de su hora.
- Una programación apunta a un solo puerto. Si el puerto está ocupado con otra ejecución cuando llega la hora, el programador salta ese ciclo y lo intenta en el siguiente.
- Dos programaciones no pueden compartir una franja horaria en el mismo puerto; la app lo rechaza con “Time slot is occupied on this port”.
- Pueden ejecutarse hasta 20 tareas programadas a la vez.
- Con un Número de repeticiones mayor que 1, una ejecución fallida (salida distinta de cero) detiene las repeticiones restantes y marca la tarea como Fallida.
- Las ejecuciones programadas usan los argumentos guardados en la tarjeta del dispositivo, o el perfil de ajustes que sigue el dispositivo. Consulta Perfiles de ajustes.
- Ver historial en una tarea muestra cada ejecución con su estado y sus logs.
La página Programador cubre cómo editar, saltar y cancelar tareas.
Ejecutarla en un grupo
Para ejecutar la misma macro en varias instancias a mano, ponlas en un grupo de dispositivos, asigna la macro a cada dispositivo y haz clic en Iniciar todos. Los dispositivos arrancan uno tras otro, con medio segundo de separación, y cada uno tiene su propia ejecución, sus logs y su código de salida. Para programar el grupo, crea una tarea programada por puerto con la misma macro y la misma hora. La guía de multi instancia cubre los argumentos, los proxies y el almacenamiento por dispositivo.
Qué puede salir mal
El bucle nunca termina
Un while True sin límite, o una condición de parada que depende de una plantilla que nunca aparece. Añade un presupuesto de tiempo con time.monotonic() y un contador de fallos consecutivos; esos dos terminan cualquier bucle.
La programación no se disparó
La app estaba cerrada o suspendida a esa hora, el puerto estaba ocupado o la tarea no está Activa. Revisa Ver historial en busca de una entrada saltada o fallida y mantén el ordenador despierto durante la ventana programada.
Time slot is occupied on this port
Otra tarea en el mismo puerto ya tiene esa hora. Mueve una tarea unos minutos, o mete la segunda macro dentro de la primera con el Número de repeticiones.
La ejecución aparece como Fallida pero la macro terminó
El script terminó con un código de salida distinto de cero o con una excepción no controlada después de su última acción. Lee las últimas líneas del log; un sys.exit(1) en una ruta de limpieza o un KeyError sobre el estado guardado son las causas habituales.
Ninguna herramienta de automatización está libre de riesgos al 100 %, así que automatiza con responsabilidad y bajo tu propio criterio.
Siguientes pasos
Páginas relacionadas
Gracias. Si algo está mal, cuéntanoslo en Discord.
¿Dudas? Pregunta en Discord