Guias
LDPlayer macro loop: condições de parada e agendamento
Coloque uma macro em loop no LDPlayer ou BlueStacks com condições de parada, guarde o progresso entre execuções, saia com um código de status e agende no MAS.
- Windows
- Mac
- Emulador
- Studio
- SDK Python
Nesta página
- Antes de começar
- Padrões de macro loop para LDPlayer e BlueStacks
- Condições de parada do macro loop
- Pausas e tempo humanizado
- Guarde o progresso com o armazenamento
- Saia com um código para os webhooks informarem o status
- Um exemplo completo
- Agende a macro no Agendador do MAS no BlueStacks ou LDPlayer
- Rode em um grupo
- O que pode dar errado
- O loop nunca termina
- O agendamento não disparou
- Time slot is occupied on this port
- A execução mostra Falhou mas a macro terminou
Uma macro que roda sem supervisão precisa de três coisas: um loop que sabe quando parar, um jeito de lembrar onde chegou e um agendamento que a inicia sem você. Este guia cobre as três para uma macro LDPlayer ou BlueStacks no Macro Automation Studio (MAS). Você recebe os padrões de loop, as condições de parada, o armazenamento entre execuções, os códigos de saída que os webhooks podem informar e o Agendador. Ele é para quem está levando uma macro de “eu clico em Executar” para “ela roda toda manhã”.
Antes de começar
- O MAS está instalado e você fez login. Ele é um download gratuito para o teste.
- Um projeto Baseado em código roda em um emulador a partir do Code Editor. Veja Primeiros passos.
- O emulador está adicionado como dispositivo com uma porta que você encontra no card dele. Veja Dispositivos.
- Seus templates e regiões estão capturados. Veja o guia de reconhecimento de imagem.
Padrões de macro loop para LDPlayer e BlueStacks
Três formas cobrem quase toda macro.
While, com uma condição de parada. O loop roda até algo na tela ou nos seus contadores mandar parar.
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, com uma contagem fixa. Você sabe quantas vezes fazer a coisa.
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)Até um template aparecer. Uma espera com prazo, usada para telas de carregamento e timers longos.
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 NoneTodo loop acima tem uma saída. find_object retorna None quando falha e nunca lança exceção, então um template ausente sozinho não consegue encerrar um while True. Dê a todo loop um limite.
Condições de parada do macro loop
Combine pelo menos duas destas. Uma protege contra o jogo, a outra contra o seu próprio bug.
- Um contador.
while collected < 50. Barato e previsível. - Um limite de tempo.
time.monotonic()conta segundos e nunca pula quando o relógio muda. Use-o no loop externo para uma execução agendada sempre terminar antes de a próxima começar. - Um estado de tela. Um template como “sem energia”, “inventário cheio” ou uma mensagem de limite diário. Capture-o no Asset Lab como qualquer outro template e confira-o no topo de cada iteração.
- Falhas consecutivas. Conte quantas iterações seguidas não encontraram nada e desista depois de cinco. Uma tela que você não previu se parece exatamente com isso.
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 e tempo humanizado
click já espera delay_ms=1000 depois do toque e swipe leva duration_ms=1000 por padrão. Entre as iterações, adicione uma pausa que varia.
import random
def pause(low=0.8, high=2.5):
time.sleep(random.uniform(low, high))Intervalos fixos parecem mecânicos e também disputam com o jogo. Um pop-up que aparece 900 ms depois de um toque escapa de um atraso fixo de 1000 ms em um dia lento. Uma pausa aleatória suaviza os dois. Mantenha delay_ms no click para o tempo de reação do app e use pause() para o ritmo humano.
Guarde o progresso com o armazenamento
Uma execução agendada que para no meio deve retomar de onde parou na próxima vez. As funções de armazenamento guardam um pequeno documento JSON por tarefa, indexado por este computador, pela porta do dispositivo e por um nome de tarefa que você escolhe.
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) grava ou substitui a entrada; retrieve(task_name) retorna o dicionário ou um vazio; clear(task_name) o apaga. data precisa ser serializável em JSON. Como a porta faz parte da chave, duas instâncias de emulador rodando a mesma macro nunca compartilham um contador. retrieve_all(task_name) retorna a entrada de toda instância quando você quer um total. Veja Armazenamento.
Saia com um código para os webhooks informarem o status
O MAS lê o código de saída do processo quando o seu script termina. Zero significa que a execução foi concluída; qualquer outro valor a marca como falha. Os inscritos em webhooks recebem macro.completed ou macro.failed conforme o caso, com o exit_code no 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.completedUma exceção não tratada também sai com valor diferente de zero, então uma falha é informada como falha sem nenhum código extra. Para eventos que só o seu script conhece, mas.webhook("level.reached", {"level": 40}) envia um evento custom.level.reached aos endpoints inscritos em eventos personalizados. Configure os endpoints na página Webhooks; veja Webhooks.
Um exemplo completo
A macro coleta até cinquenta recompensas por dia, lembra a contagem, para em “sem energia” ou depois de vinte minutos e informa pelo código de saída. Troque os IDs pelos seus.
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()Rode uma vez pelo Code Editor com Executar (F5) e confirme que ela termina sozinha antes de agendá-la.
Agende a macro no Agendador do MAS no BlueStacks ou LDPlayer
O Agendador vive dentro do app de desktop e inicia macros nos seus emuladores nos horários que você define.
- Abra o Agendador e clique em Criar novo agendamento.
- Digite um Nome (3 a 100 caracteres) e escolha a Macro.
- Em Porta do emulador, clique em Escanear e escolha a porta da instância, ou digite-a.
- Defina Data e Hora. A hora é a hora local do seu computador.
- Defina a Recorrência: Nenhuma para uma execução única, Diária, Semanal com os Dias da semana marcados, ou Mensal.
- Deixe Contagem de repetições em 1, a menos que queira que a macro rode várias vezes seguidas a cada disparo.
- Mantenha Ativo ligado e clique em Criar agendamento.
O que saber antes de confiar nele:
- O app precisa ficar aberto e com login feito. O agendador não é um serviço do sistema e não há sintaxe de cron.
- Os jobs são verificados a cada 30 segundos, então uma execução pode começar até meio minuto depois do horário.
- Um agendamento tem uma porta como alvo. Se a porta estiver ocupada com outra execução na hora, o agendador pula esse ciclo e tenta de novo no próximo.
- Dois agendamentos não podem compartilhar um horário na mesma porta; o app recusa com “Time slot is occupied on this port”.
- Até 20 jobs agendados podem rodar ao mesmo tempo.
- Com Contagem de repetições acima de 1, uma execução com falha (saída diferente de zero) interrompe as repetições restantes e marca o job como Falhou.
- As execuções agendadas usam os argumentos salvos no card do dispositivo, ou o perfil de configurações que o dispositivo segue. Veja Perfis de configurações.
- Ver histórico em um job mostra toda execução com status e logs.
A página Agendador cobre editar, pular e cancelar jobs.
Rode em um grupo
Para rodar a mesma macro em várias instâncias à mão, coloque-as em um grupo de dispositivos, atribua a macro a cada dispositivo e clique em Iniciar todos. Os dispositivos iniciam um após o outro, com meio segundo de intervalo, e cada um tem a própria execução, os próprios logs e o próprio código de saída. Para agendar o grupo, crie um job agendado por porta com a mesma macro e o mesmo horário. O guia de multi-instância cobre argumentos, proxies e armazenamento por dispositivo.
O que pode dar errado
O loop nunca termina
Um while True sem limite, ou uma condição de parada que depende de um template que nunca aparece. Adicione um limite de tempo com time.monotonic() e um contador de falhas consecutivas; esses dois encerram qualquer loop.
O agendamento não disparou
O app estava fechado ou suspenso na hora, a porta estava ocupada, ou o job não está Ativo. Confira Ver histórico para uma entrada pulada ou com falha, e mantenha o computador acordado na janela agendada.
Time slot is occupied on this port
Outro job na mesma porta já é dono desse horário. Mova um job alguns minutos, ou coloque a segunda macro dentro da primeira com Contagem de repetições.
A execução mostra Falhou mas a macro terminou
O script terminou com um código de saída diferente de zero ou uma exceção não tratada depois da última ação. Leia as últimas linhas do log; um sys.exit(1) em um caminho de limpeza ou um KeyError no estado guardado são as causas habituais.
Nenhuma ferramenta de automação é 100% livre de riscos, por isso automatize com responsabilidade e a seu próprio critério.
Próximos passos
Páginas relacionadas
Obrigado. Se algo estiver errado, conte para a gente no Discord.
Dúvidas? Pergunte no Discord