검색

가이드

LD플레이어 매크로 반복, 정지 조건, 예약 실행

LDPlayer나 BlueStacks에서 매크로를 정지 조건과 함께 반복하고, 실행 사이에 진행 상황을 저장하며, 종료 코드를 남기고 MAS 스케줄러로 매일 예약 실행합니다.

  • Windows
  • Mac
  • 에뮬레이터
  • 스튜디오
  • Python SDK
초급 업데이트 8분 소요
이 페이지의 내용
  1. 시작하기 전에
  2. LD플레이어와 BlueStacks용 매크로 반복 패턴
  3. 매크로 반복 정지 조건
  4. 쉼과 사람다운 타이밍
  5. 스토리지로 진행 상황 유지
  6. 웹훅이 상태를 보고하도록 코드와 함께 종료
  7. 완전한 예제
  8. BlueStacks나 LDPlayer에서 MAS 스케줄러로 매크로 예약 실행
  9. 그룹에서 실행
  10. 잘못될 수 있는 것
  11. 반복문이 끝나지 않음
  12. 예약이 실행되지 않음
  13. Time slot is occupied on this port
  14. 매크로는 끝났는데 실행이 Failed로 표시됨

혼자 돌아가는 매크로에는 세 가지가 필요합니다: 언제 멈출지 아는 반복문, 어디까지 했는지 기억하는 방법, 그리고 사용자 없이 시작하는 예약. 이 가이드는 Macro Automation Studio(MAS)의 LD플레이어 매크로 반복과 BlueStacks 매크로 반복에 대해 이 세 가지를 모두 다룹니다. LD플레이어 매크로 반복 패턴, 정지 조건, 실행 사이의 스토리지, 웹훅이 보고할 수 있는 종료 코드, 그리고 스케줄러를 얻게 됩니다. 매크로를 “내가 Run을 클릭한다”에서 “매일 아침 알아서 돈다”로 옮기려는 분을 위한 가이드입니다.

시작하기 전에

  • MAS가 설치되어 있고 로그인한 상태. 체험용은 무료 다운로드입니다.
  • Code-Based 프로젝트가 Code Editor에서 에뮬레이터로 실행되는 상태. 시작하기를 참고하세요.
  • 에뮬레이터가 카드에서 포트를 확인할 수 있는 기기로 추가된 상태. 기기를 참고하세요.
  • 템플릿과 영역이 캡처된 상태. 이미지 인식 가이드를 참고하세요.

LD플레이어와 BlueStacks용 매크로 반복 패턴

세 가지 형태가 거의 모든 매크로를 다룹니다.

정지 조건이 있는 while. 화면이나 카운터가 멈추라고 할 때까지 반복합니다.

python
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 += 1

고정 횟수 for. 몇 번 할지 알고 있을 때입니다.

python
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)

템플릿이 나타날 때까지. 마감이 있는 대기로, 로딩 화면과 긴 타이머에 씁니다.

python
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 None

위의 모든 반복문에는 출구가 있습니다. find_object는 놓치면 None을 반환하고 예외를 던지지 않으므로, 템플릿이 없다는 것만으로는 while True를 끝낼 수 없습니다. 모든 반복문에 한계를 주세요.

매크로 반복 정지 조건

다음 중 최소 두 개를 조합하세요. 하나는 게임을, 다른 하나는 본인의 버그를 막습니다.

  • 카운터. while collected < 50. 저렴하고 예측 가능합니다.
  • 시간 예산. time.monotonic()은 초를 세고 시계가 바뀌어도 건너뛰지 않습니다. 바깥 반복문에 써서 예약 실행이 다음 실행 전에 항상 끝나게 하세요.
  • 화면 상태. “out of energy”, “inventory full” 또는 일일 한도 메시지 같은 템플릿. 다른 템플릿처럼 Asset Lab에서 캡처하고 각 회차 맨 위에서 확인하세요.
  • 연속 실패. 연속으로 아무것도 찾지 못한 회차를 세고 다섯 번이면 포기하세요. 계획하지 않은 화면은 정확히 이렇게 보입니다.
python
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)

쉼과 사람다운 타이밍

click은 이미 탭 후 delay_ms=1000을 기다리고 swipe는 기본적으로 duration_ms=1000 동안 진행됩니다. 회차 사이에는 변하는 쉼을 넣으세요.

python
import random

def pause(low=0.8, high=2.5):
    time.sleep(random.uniform(low, high))

고정 간격은 기계적으로 보이고 게임과 경합하기도 합니다. 탭 900 ms 뒤에 나타나는 팝업은 느린 날에 고정 1000 ms 지연을 빠져나갑니다. 무작위 쉼이 둘 다 부드럽게 합니다. 앱 자체 반응 시간에는 clickdelay_ms를 유지하고, 사람의 리듬에는 pause()를 쓰세요.

스토리지로 진행 상황 유지

중간에 멈춘 예약 실행은 다음에 멈춘 곳에서 이어져야 합니다. 스토리지 함수는 이 컴퓨터, 기기 포트, 직접 고른 작업 이름을 키로 작업마다 작은 JSON 문서를 보관합니다.

python
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 time

save(task_name, data)는 항목을 쓰거나 대체하고, retrieve(task_name)은 딕셔너리 또는 빈 딕셔너리를 반환하고, clear(task_name)은 삭제합니다. data는 JSON으로 직렬화할 수 있어야 합니다. 포트가 키의 일부이므로 같은 매크로를 실행하는 에뮬레이터 인스턴스 두 개는 절대 카운터를 공유하지 않습니다. 합계가 필요하면 retrieve_all(task_name)이 모든 인스턴스의 항목을 반환합니다. 스토리지를 참고하세요.

웹훅이 상태를 보고하도록 코드와 함께 종료

스크립트가 끝나면 MAS가 프로세스 종료 코드를 읽습니다. 0은 실행 완료를 뜻하고, 그 밖의 값은 실패로 표시됩니다. 웹훅 구독자는 그에 따라 페이로드에 exit_code가 담긴 macro.completed 또는 macro.failed를 받습니다.

python
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.completed

처리되지 않은 예외도 0이 아닌 값으로 종료되므로 추가 코드 없이 충돌이 실패로 보고됩니다. 스크립트만 아는 이벤트에는 mas.webhook("level.reached", {"level": 40})이 사용자 지정 이벤트를 구독한 엔드포인트에 custom.level.reached 이벤트를 보냅니다. 엔드포인트는 Webhooks 페이지에서 설정합니다. 웹훅을 참고하세요.

완전한 예제

매크로는 하루에 보상을 최대 50개 모으고, 개수를 기억하고, “out of energy”나 20분 뒤에 멈추고, 종료 코드로 보고합니다. ID를 본인 것으로 바꾸세요.

python
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()

예약하기 전에 Code Editor에서 Run(F5)으로 한 번 실행해 스스로 끝나는지 확인하세요.

BlueStacks나 LDPlayer에서 MAS 스케줄러로 매크로 예약 실행

스케줄러는 데스크톱 앱 안에 있으며 설정한 시간에 에뮬레이터에서 매크로를 시작합니다.

  1. Scheduler를 열고 Create New Schedule을 클릭합니다.
  2. Name(3자에서 100자)을 입력하고 Macro를 고릅니다.
  3. Emulator Port에서 Scan을 클릭해 인스턴스의 포트를 고르거나 직접 입력합니다.
  4. DateTime을 설정합니다. 시간은 컴퓨터의 현지 시간입니다.
  5. Recurrence를 설정합니다: 한 번 실행은 None, Daily, Days of Week를 체크한 Weekly, 또는 Monthly.
  6. 매 실행 시각마다 매크로를 여러 번 연달아 실행하고 싶지 않다면 Repeat Count를 1로 둡니다.
  7. Active를 켠 채 Create Schedule을 클릭합니다.

의지하기 전에 알아 둘 것:

  • 앱이 열려 있고 로그인되어 있어야 합니다. 스케줄러는 시스템 서비스가 아니며 cron 문법도 없습니다.
  • 작업은 30초마다 확인되므로 실행이 지정 시간보다 최대 30초 늦게 시작할 수 있습니다.
  • 예약은 포트 하나를 대상으로 합니다. 시간이 되었을 때 포트가 다른 실행으로 바쁘면 스케줄러는 그 회차를 건너뛰고 다음 회차에 다시 시도합니다.
  • 같은 포트에서 두 예약이 같은 시간대를 공유할 수 없습니다. 앱이 “Time slot is occupied on this port”로 거부합니다.
  • 예약된 작업은 동시에 최대 20개까지 실행할 수 있습니다.
  • Repeat Count가 1보다 크면 실패한 실행(0이 아닌 종료)이 남은 반복을 멈추고 작업을 Failed로 표시합니다.
  • 예약 실행은 기기 카드에 저장된 인수, 또는 기기가 따르는 설정 프로필을 사용합니다. 설정 프로필을 참고하세요.
  • 작업의 View History는 모든 실행을 상태와 로그와 함께 보여 줍니다.

작업 편집, 건너뛰기, 취소는 스케줄러 페이지에서 다룹니다.

그룹에서 실행

같은 매크로를 여러 인스턴스에서 손으로 실행하려면 기기 그룹 하나에 넣고, 각 기기에 매크로를 지정하고, Start All을 클릭하세요. 기기가 0.5초 간격으로 차례로 시작되며 각각 자기 실행, 로그, 종료 코드를 가집니다. 그룹을 예약하려면 같은 매크로와 시간으로 포트마다 예약 작업 하나를 만드세요. 멀티 인스턴스 가이드에서 기기별 인수, 프록시, 스토리지를 다룹니다.

잘못될 수 있는 것

반복문이 끝나지 않음

한계 없는 while True이거나, 끝내 나타나지 않는 템플릿에 의존하는 정지 조건입니다. time.monotonic() 시간 예산과 연속 실패 카운터를 추가하세요. 그 둘이 모든 반복문을 끝냅니다.

예약이 실행되지 않음

그 시간에 앱이 닫혀 있거나 절전 상태였거나, 포트가 바빴거나, 작업이 Active가 아닙니다. View History에서 건너뛴 항목이나 실패한 항목을 확인하고, 예약된 시간대에 컴퓨터를 깨어 있게 하세요.

Time slot is occupied on this port

같은 포트의 다른 작업이 이미 그 시간을 갖고 있습니다. 한 작업을 몇 분 옮기거나, 두 번째 매크로를 Repeat Count로 첫 번째 안에 넣으세요.

매크로는 끝났는데 실행이 Failed로 표시됨

스크립트가 마지막 동작 뒤에 0이 아닌 종료 코드나 처리되지 않은 예외로 끝났습니다. 로그의 마지막 줄을 읽어 보세요. 정리 경로의 sys.exit(1)이나 저장된 상태의 KeyError가 흔한 원인입니다.

100% 안전한 자동화 도구는 없으므로, 책임감을 가지고 본인의 판단에 따라 자동화하세요.

다음 단계

관련 페이지

이 페이지가 도움이 되었나요?

궁금한 점이 있으신가요? Discord에서 질문하기