javascriptperformancebotspuppeteerwindow-object

Online game bot performance


I'm creating bot for online dynamic game. In this case dynamic means that hero ingame can move around and background is changing when moving. Global variables like monsters are changing dynamic when moving as well.

My bot is using puppeteer. Since I need this monsters object I have function which get those monsters from page context every 2 - 3 seconds (randomize for anti-detection).

This solution is far from being perfect. Two main downsides are:

To solve first one I could just execute function which download monsters every time after killing one. On the other hand then second downside will be even stronger because I will perform much more getting monster which already is slow.

It all comes to second issue which is performance. You may ask how do I know that performance is bad?

When hero is moving it's relatively smooth but when monsters are being downloaded I see micro lag, like for a part of second hero stop. It's really maybe 100ms of lag but I can see it with human eye and if I will perform getting monster more frequently this lag will get stronger (to be clear - lag will not be longer but more often).

Downloading object from global window is long. The reason why is that game maintainers developed it so monsters are within big object npc which contains everything that is in dashboard and contains even empty elements so the total amount of this npc object is between 100k-200k elements. I'm doing lots of filters in order to get final monster data which I care about.

I'll show how I'm actually getting this monsters. So I execute 3 async functions:

const allNpc = await getAllNpc(page);
let monsters = await filterMonsters(page, allNpc, monstersToHunt);
monsters.hunt = await deleteAvoidedMonsters(page, monsters.hunt, monstersToOmit);

First one getAllNpc just get entire npc object (this big one which I mentioned above)

return await page.evaluate(() => {
    if(window.g) return g.npc;
});

second function filter actual monsters and monsters which I want to kill from 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});
});

I'm using Set here to get rid of O(n) / O(n^2) algorithms and I think this is fastest I can achieve with javascript. Third function is same as this one but additionally filtering special monsters which I want to avoid.

Now my questions are:


Solution

  • What I realized after a while is that bot was "lagging" because I was passing this huge g.npc array as argument to function. By that I mean I was resolving it from getAllNpc and then passing it to filterMonsters.

    When I changed it so I execute .filter in getAllNpc inside evaluated script within page context and then resolving and passing array with hundreds not millions elements it is much faster and do not cause any lag or freeze.