搜索

教程

雷电模拟器脚本循环、停止条件与定时运行

让 LDPlayer 或 BlueStacks 上的宏带停止条件循环,在运行之间保存进度,用状态码退出,并在 MAS 定时任务中每天运行。

  • Windows
  • Mac
  • 模拟器
  • Studio
  • Python SDK
入门 更新于 阅读约 7 分钟
本页目录
  1. 开始之前
  2. LDPlayer 和 BlueStacks 的脚本循环模式
  3. 脚本循环的停止条件
  4. 停顿与拟人化节奏
  5. 用存储保存进度
  6. 用退出码退出,让 Webhook 报告状态
  7. 一个完整的示例
  8. 在 BlueStacks 或 LDPlayer 上用 MAS 定时任务运行宏
  9. 在组上运行
  10. 可能出什么问题
  11. 循环永远不结束
  12. 定时没有触发
  13. Time slot is occupied on this port
  14. 运行显示失败,但宏完成了

一个无人值守运行的宏需要三样东西:知道何时停止的循环、记住进行到哪里的方法,以及不用你动手就启动它的定时。这篇指南为 Macro Automation Studio(MAS)中的雷电模拟器脚本循环(LDPlayer 或 BlueStacks 上都一样)讲清这三样。你会得到循环模式、停止条件、运行之间的存储、Webhook 可以报告的退出码,以及定时任务。适合把宏从”我点运行”变成”它每天早上自己跑”的人。

开始之前

  • 已安装并登录 MAS。试用期内可免费下载
  • 一个代码型项目能从 Code Editor 在模拟器上运行。参见快速开始
  • 模拟器已添加为设备,端口可以在它的卡片上找到。参见设备
  • 你的模板和区域已经截取。参见图像识别指南

LDPlayer 和 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。给每个循环一个边界。

脚本循环的停止条件

至少组合其中两个。一个防游戏,另一个防你自己的 bug。

  • 计数器。 while collected < 50。便宜且可预测。
  • 时间预算。 time.monotonic() 按秒计数,时钟变化时也不会跳变。把它用在外层循环,这样定时运行总会在下一次开始前结束。
  • 屏幕状态。 一个模板,比如”体力不足”、“背包已满”或每日上限提示。像其他模板一样在 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=1000swipe 默认持续 duration_ms=1000。在轮次之间,加入一个有变化的停顿。

python
import random

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

固定间隔看起来很机械,还会和游戏抢时间。点击后 900 毫秒才出现的弹窗,在慢的日子里会从固定的 1000 毫秒延迟中溜过去。随机停顿两者都能缓和。click 上的 delay_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) 返回每个实例的条目。参见存储

用退出码退出,让 Webhook 报告状态

脚本结束时 MAS 读取进程的退出码。零表示运行完成;其他任何值都标记为失败。Webhook 订阅者相应地收到 macro.completedmacro.failed,载荷里带 exit_code

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

未处理的异常也会以非零退出,所以崩溃无需额外代码就会被报告为失败。对于只有你的脚本知道的事件,mas.webhook("level.reached", {"level": 40}) 会向订阅了自定义事件的端点发送一个 custom.level.reached 事件。在 Webhook 页设置端点;参见 Webhook

一个完整的示例

这个宏每天最多领取五十个奖励,记住自己的计数,在”体力不足”或二十分钟后停止,并通过退出码报告。把 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 中用运行F5)跑一次,确认它能自己结束,再设置定时。

在 BlueStacks 或 LDPlayer 上用 MAS 定时任务运行宏

定时任务位于桌面应用内部,在你设定的时间在模拟器上启动宏。

  1. 打开定时任务,点击新建计划
  2. 输入名称(3 到 100 个字符),选择
  3. 模拟器端口下点击扫描并选择实例的端口,或者直接输入。
  4. 设置日期时间。时间是你电脑的本地时间。
  5. 设置重复表示只运行一次,每天每周并勾选星期几,或者每月
  6. 重复次数保持为 1,除非你想让宏在每次触发时连续运行多次。
  7. 保持启用打开,点击创建计划

依赖它之前要知道的事:

  • 应用必须保持打开并登录。定时任务不是系统服务,也没有 cron 语法。
  • 任务每 30 秒检查一次,所以运行可能比设定时间晚最多半分钟开始。
  • 一个计划针对一个端口。到时间时端口正被另一次运行占用,定时任务会跳过这一轮,下一轮再试。
  • 两个计划不能在同一个端口共用一个时间段;应用会拒绝并提示 “Time slot is occupied on this port”。
  • 最多 20 个定时任务可以同时运行。
  • 重复次数大于 1 时,一次失败的运行(非零退出)会停止剩余的重复,并把任务标记为失败。
  • 定时运行使用设备卡片上保存的参数,或设备跟随的设置方案。参见设置方案
  • 任务上的查看历史显示每次运行及其状态和日志。

定时任务页介绍编辑、跳过和取消任务。

在组上运行

要手动在多个实例上运行同一个宏,把它们放进一个设备组,给每台设备分配宏,点击全部启动。设备依次启动,间隔半秒,每台都有自己的运行、日志和退出码。要为组设置定时,为每个端口创建一个宏和时间相同的定时任务。多开指南介绍按设备的参数、代理和存储。

可能出什么问题

循环永远不结束

一个没有边界的 while True,或者一个依赖永远不会出现的模板的停止条件。加上用 time.monotonic() 的时间预算和连续失败计数器;这两个能结束任何循环。

定时没有触发

到时间时应用已关闭或处于休眠、端口被占用,或者任务没有启用。在查看历史中查找跳过或失败的条目,并在定时窗口内让电脑保持唤醒。

Time slot is occupied on this port

同一个端口上的另一个任务已经占用了那个时间。把一个任务挪开几分钟,或者用重复次数把第二个宏放进第一个里。

运行显示失败,但宏完成了

脚本在最后一个动作之后以非零退出码或未处理的异常结束。阅读日志的最后几行;清理路径里的 sys.exit(1),或存储状态上的 KeyError,是常见原因。

没有任何自动化工具是 100% 无风险的,请负责任地使用自动化,并自行斟酌决定。

下一步

相关页面

这个页面有帮助吗?

有疑问? 在 Discord 提问