教程
模拟器多开挂机脚本:雷电、蓝叠多开同步运行
MAS 中的模拟器多开挂机:按端口添加每个 BlueStacks 或 LDPlayer 实例,编成设备组,给每台设备自己的设置方案和代理,一键同步运行。
- Windows
- Mac
- 模拟器
- Studio
- Python SDK
本页目录
一个宏、五个账号、一次点击:这就是模拟器多开挂机。Macro Automation Studio(MAS)把每个模拟器实例当作一台单独的设备。设备组随后在所有实例上运行同一个宏,每台设备有自己的参数、各自的代理,以及永远不会混淆计数器的存储。这篇指南在 BlueStacks、LDPlayer、MuMu Player 或 MEmu 上搭好这套多开挂机方案。适合运行不止一个账号的人。
开始之前
- 已安装并登录 MAS,套餐的设备数覆盖你想同时运行的实例数。参见计费。
- 模拟器的多开管理器已经创建了实例,每个实例都开启了 ADB。BlueStacks、LDPlayer、MuMu Player 和 MEmu 指南说明设置和端口在哪里。
- 每个实例使用与你截取模板时相同的分辨率和 DPI。
- 宏已经能从 Code Editor 在一个实例上工作。参见循环指南。
多开模拟器的每个实例都是一台设备
每个模拟器实例在自己的本地 TCP 端口上监听 adb。MAS 通过扫描运行中的进程并保留各模拟器已知范围内的端口来发现实例。所以两个 BlueStacks 窗口会在端口列表中显示为两项。设备就是端口;组就是端口的列表。
| 模拟器 | MAS 接受的端口 |
|---|---|
| BlueStacks | 5555 到 8500 |
| LDPlayer | 5555 到 5599 之间的奇数端口 |
| MuMu Player | 5555 到 5599 的奇数端口、7555、16384 到 16576、26624 到 27264 |
| MEmu | 21503 及其之上、30000 以下所有以 3 结尾的端口 |
添加时保持每个实例打开;已停止的实例没有可供发现的端口。完整的发现规则见设备页。
创建设备组
- 打开设备组,点击新建组。
- 输入名称,类型保持本地,点击创建组。
- 打开这个组,点击添加设备。
- 输入一个能说明是哪个账号的设备名称,例如
farm-alt-2。 - 在端口下选择实例的端口。缺失时点击刷新,或选择自定义端口并输入。
- 点击添加设备,对每个实例重复。
- 在每张设备卡片上,在宏选择器中选择宏。
一个端口只能属于一台设备。重复添加会失败并提示 “Port is already in use”。参见设备组。
给每台设备自己的参数
大多数脚本需要一个按账号不同的值:一个名字、一个目标、一个轮次数。有两种传递方式。
设置方案(不改代码)。 如果宏有 UI Builder 做的参数表单,每张设备卡片都有编辑参数。对话框带一个方案栏:输入值,选择将当前值另存为新方案,命名并点击创建方案。一台设备跟随一个方案,编辑方案会在下一次运行时更新所有跟随它的设备。“Default” 始终存在且不能删除。参见设置方案和 UI Builder。
代码中的端口。 脚本可以询问自己在哪个实例上,并选择自己的设置。这不需要表单,结尾的示例就是这么做的。
import mas
port = mas.get_current_device_port() # 5555, 5557, 5559 ...值经常变化或者由别人运行脚本时用方案;映射固定且和代码放在一起时用端口。
给每台设备自己的代理
共用一个 IP 的账号共担同一个命运。MAS 为每台设备绑定一个代理,并把该实例的流量路由过去。
- 打开代理。在添加代理下,填写网关账户标签页,或者用其他提供商标签页填入你自己的代理(
http、https或socks5),点击添加并测试。 - 等待测试。它确认代理有响应并检测它的国家;之后可以用测试重复。
- 点击分配并选择设备,或者在设备组中打开设备卡片,在代理下选择代理。
设备卡片在每次运行时显示代理状态,运行 Webhook 携带 proxy_exit_ip 和 proxy_country。一台设备持有一个代理;绑定另一个会替换它。参见代理。
全部启动:LDPlayer 或 BlueStacks 的多开同步运行
点击组顶部的全部启动。MAS 依次启动设备,间隔半秒,每台都有自己的运行、自己的日志和自己的退出码。没有宏的设备会带着错误被跳过,其余的继续。全部停止结束组里的每次运行。每张卡片上的日志标签页只显示这台设备,宏有运行时 UI 的话,仪表盘标签页会显示它。
用存储保存按设备的状态
mas.save、mas.retrieve 和 mas.clear 的键由这台电脑、设备的端口和任务名组成。所以运行同一个宏的两个实例无需额外代码就保持各自独立的计数器。要把它们加起来,mas.retrieve_all(task_name) 为每个端口返回一个条目,带 machine_id、port、data 和 updated_at。
TASK = "collector"
mine = mas.retrieve(TASK) # this instance only
everyone = mas.retrieve_all(TASK) # every instance on this computer
total = sum(entry["data"].get("done", 0) for entry in everyone)参见存储。
按组设置 Webhook
要在任何账号完成或失败时得到通知,在 Webhook 页添加一个端点,在发送给我下选择事件,在接收来自以下来源的事件下选择指定组并勾选这个组。之后加入组的设备也会被覆盖。每个事件携带 device_name、device_port、group_id、status 和 exit_code,所以一个端点就能区分账号。参见 Webhook。
注意蓝叠多开挂机的资源限制
每个实例都是一个完整的安卓系统。扩大规模之前,检查三件事:
- 每个实例的 CPU 和内存。 每个实例占用模拟器实例设置中设定的核心和内存。把它们加起来,并给 MAS 留出余量,因为它在同一台机器上为每个实例做模板匹配和 OCR。当点击开始延迟落地时,你就超了。
- 套餐的设备数。 套餐按你自己的设备可以同时运行多少台计量。全部启动只运行套餐允许的数量;参见计费和价格页。
- 定时任务。 定时任务最多同时运行 20 个任务,每个端口一个。
干净运行的少数实例,胜过匹配不上模板的更多实例。
为组设置定时
定时任务每个任务针对一个端口,所以组的定时就是每台设备一个任务。
- 打开定时任务,点击新建计划。
- 选择宏,在模拟器端口下点击扫描并选择第一个实例。
- 设置日期、时间和重复,然后点击创建计划。
- 对每个端口重复。给这些任务相同的时间;不同端口永远不会冲突。
定时运行使用每张设备卡片上保存的参数,或设备跟随的方案,所以按账号的值会带过去。应用必须保持打开。参见定时任务和循环指南。
一个完整的示例
这个宏读取自己的端口,选择该账号的设置,保存按端口的计数器,并在结束时记录所有实例的总数。把端口和图片 ID 换成你的。
import random
import sys
import time
import mas
images = mas.images({
"collect": 501,
"close_popup": 502,
})
SETTINGS = { # per instance, keyed by the emulator's adb port
5555: {"account": "main", "rounds": 20},
5557: {"account": "alt-1", "rounds": 10},
5559: {"account": "alt-2", "rounds": 10},
}
DEFAULT = {"account": "unknown", "rounds": 5}
TASK = "collector"
TIME_BUDGET_S = 15 * 60
def main():
port = mas.get_current_device_port()
settings = SETTINGS.get(port, DEFAULT)
account = settings["account"]
mas.log(f"Port {port}: account {account}, {settings['rounds']} rounds")
done = mas.retrieve(TASK).get("done", 0)
started = time.monotonic()
misses = 0
while done < settings["rounds"]:
if time.monotonic() - started > TIME_BUDGET_S:
mas.log(f"{account}: 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
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(f"{account}: five misses, giving up", level="error")
sys.exit(2)
continue
misses = 0
mas.click(button.x, button.y, delay_ms=1000)
done += 1
mas.save(TASK, {"done": done, "account": account})
time.sleep(random.uniform(0.8, 2.5))
total = sum(entry["data"].get("done", 0) for entry in mas.retrieve_all(TASK))
mas.log(f"{account}: {done} rounds done; {total} across all instances")
mas.webhook("account.finished", {"account": account, "port": port, "done": done})
if __name__ == "__main__":
main()mas.webhook 向订阅了自定义事件的端点发送一个 custom.account.finished 事件;没有端点的话去掉这一行。映射变多时,把它移进参数表单,改读 args.<tab>.<key> 而不是字典。
可能出什么问题
Port is already in use
你的某个组里已有另一台设备占用了这个端口。每个端口只能添加一次;删除旧条目,或改选另一个实例的端口。
一个实例在运行,其他的停在连接中
其他实例的 ADB 是关闭的、还在启动,或者监听的端口在模拟器范围之外。打开每个实例的设置,确认 ADB 和端口,在添加设备中点击刷新。端口在上表之外时,用自定义端口。参见 ADB 故障排查。
模板只在一个实例上匹配
那个实例的分辨率或 DPI 不同。模板匹配基于像素;在模拟器的实例设置中把每个实例设为截取时的分辨率并重启。其余内容见图像识别指南。
所有实例共用一个计数器
脚本向 mas.save 传入了显式的 port 或 machine_id,或者用磁盘文件代替了存储。用默认参数调用 mas.save(TASK, data);当前端口会自动加上。
没有任何自动化工具是 100% 无风险的,请负责任地使用自动化,并自行斟酌决定。
下一步
相关页面
谢谢。如果有哪里不对,请在 Discord 告诉我们。
有疑问? 在 Discord 提问