在线游戏机器人性能
Online game bot performance
我正在为在线动态游戏创建机器人。在这种情况下,动态意味着游戏中的英雄可以四处移动,移动时背景会发生变化。 monsters
等全局变量在移动时也会动态变化。
我的机器人正在使用 puppeteer。因为我需要这个怪物对象,所以我有功能可以每 2 - 3 秒从页面上下文中获取这些怪物(随机化以防检测)。
这个解决方案远非完美。两个主要缺点是:
- 我的机器人杀死了怪物,我想去下一个它仍然看到这个怪物已经被杀死,因为下一次刷新是从那个时间点开始的,例如 1500 毫秒。
- 获取怪物的性能很差
为了解决第一个问题,我可以执行每次杀死一个怪物后下载怪物的功能。另一方面,第二个缺点会更严重,因为我会执行更多已经很慢的怪物。
这一切都涉及到第二个问题,即性能。你可能会问我怎么知道性能不好?
英雄移动时相对流畅,但在下载怪物时我看到微延迟,比如第二个英雄停止的一部分。这真的可能有 100 毫秒的滞后,但我可以用肉眼看到它,如果我更频繁地执行获取怪物,这个滞后会变得更强(要清楚 - 滞后不会更长但更频繁)。
从全局下载对象 window 很长。原因是游戏维护者开发了它,所以 monsters
在大对象 npc
中,它包含仪表板中的所有内容,甚至包含空元素,所以这个 npc
对象的总量介于100k-200k 元素。我正在做很多过滤器以获得我关心的最终怪物数据。
我会展示我是如何得到这些怪物的。所以我执行了 3 个异步函数:
const allNpc = await getAllNpc(page);
let monsters = await filterMonsters(page, allNpc, monstersToHunt);
monsters.hunt = await deleteAvoidedMonsters(page, monsters.hunt, monstersToOmit);
第一个getAllNpc
只得到整个npc对象(我上面提到的这个大的)
return await page.evaluate(() => {
if(window.g) return g.npc;
});
第二个功能过滤实际怪物和我想从npc杀死的怪物:
return new Promise((resolve, reject) => {
const validNpc = allNpc.filter(el => !!el);
const allMonsters = validNpc.filter(e => e.lvl !== 0);
const names = new Set(monstersNames);
const huntMonsters = validNpc
.filter(it => names.has(it.nick))
.map(({ nick, x, y, grp, id }) => ({ nick, x, y, grp, id }));
resolve({all: allMonsters, hunt: huntMonsters});
});
我在这里使用 Set
来摆脱 O(n) / O(n^2) 算法,我认为这是我可以用 javascript 实现的最快速度。第三个功能与这个相同,但额外过滤了我想避免的特殊怪物。
现在我的问题是:
- 有没有办法在每个 this 对象上获取 this 对象并且只有 this 对象发生变化?我知道 puppeteer 中有一个函数可以监视 DOM 变化但是有什么东西可以监视全局 window 对象吗?
- 我还能做些什么来加快速度吗?我在 NodeJS 中读到
worker_threads
,它可以帮助消除这种微延迟或其他问题吗?聚类?
一段时间后我意识到 bot 是 "lagging" 因为我将这个巨大的 g.npc
数组作为参数传递给函数。我的意思是我从 getAllNpc
解析它然后将它传递给 filterMonsters
.
当我更改它时,我在页面上下文中的评估脚本中执行 .filter
in getAllNpc
,然后解析和传递具有数亿而不是数百万元素的数组,它更快并且不会造成任何延迟或冻结。
我正在为在线动态游戏创建机器人。在这种情况下,动态意味着游戏中的英雄可以四处移动,移动时背景会发生变化。 monsters
等全局变量在移动时也会动态变化。
我的机器人正在使用 puppeteer。因为我需要这个怪物对象,所以我有功能可以每 2 - 3 秒从页面上下文中获取这些怪物(随机化以防检测)。
这个解决方案远非完美。两个主要缺点是:
- 我的机器人杀死了怪物,我想去下一个它仍然看到这个怪物已经被杀死,因为下一次刷新是从那个时间点开始的,例如 1500 毫秒。
- 获取怪物的性能很差
为了解决第一个问题,我可以执行每次杀死一个怪物后下载怪物的功能。另一方面,第二个缺点会更严重,因为我会执行更多已经很慢的怪物。
这一切都涉及到第二个问题,即性能。你可能会问我怎么知道性能不好?
英雄移动时相对流畅,但在下载怪物时我看到微延迟,比如第二个英雄停止的一部分。这真的可能有 100 毫秒的滞后,但我可以用肉眼看到它,如果我更频繁地执行获取怪物,这个滞后会变得更强(要清楚 - 滞后不会更长但更频繁)。
从全局下载对象 window 很长。原因是游戏维护者开发了它,所以 monsters
在大对象 npc
中,它包含仪表板中的所有内容,甚至包含空元素,所以这个 npc
对象的总量介于100k-200k 元素。我正在做很多过滤器以获得我关心的最终怪物数据。
我会展示我是如何得到这些怪物的。所以我执行了 3 个异步函数:
const allNpc = await getAllNpc(page);
let monsters = await filterMonsters(page, allNpc, monstersToHunt);
monsters.hunt = await deleteAvoidedMonsters(page, monsters.hunt, monstersToOmit);
第一个getAllNpc
只得到整个npc对象(我上面提到的这个大的)
return await page.evaluate(() => {
if(window.g) return g.npc;
});
第二个功能过滤实际怪物和我想从npc杀死的怪物:
return new Promise((resolve, reject) => {
const validNpc = allNpc.filter(el => !!el);
const allMonsters = validNpc.filter(e => e.lvl !== 0);
const names = new Set(monstersNames);
const huntMonsters = validNpc
.filter(it => names.has(it.nick))
.map(({ nick, x, y, grp, id }) => ({ nick, x, y, grp, id }));
resolve({all: allMonsters, hunt: huntMonsters});
});
我在这里使用 Set
来摆脱 O(n) / O(n^2) 算法,我认为这是我可以用 javascript 实现的最快速度。第三个功能与这个相同,但额外过滤了我想避免的特殊怪物。
现在我的问题是:
- 有没有办法在每个 this 对象上获取 this 对象并且只有 this 对象发生变化?我知道 puppeteer 中有一个函数可以监视 DOM 变化但是有什么东西可以监视全局 window 对象吗?
- 我还能做些什么来加快速度吗?我在 NodeJS 中读到
worker_threads
,它可以帮助消除这种微延迟或其他问题吗?聚类?
一段时间后我意识到 bot 是 "lagging" 因为我将这个巨大的 g.npc
数组作为参数传递给函数。我的意思是我从 getAllNpc
解析它然后将它传递给 filterMonsters
.
当我更改它时,我在页面上下文中的评估脚本中执行 .filter
in getAllNpc
,然后解析和传递具有数亿而不是数百万元素的数组,它更快并且不会造成任何延迟或冻结。