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:
- My bot kill monster and I want to go to next one it still see this monster which has got killed because next refresh is for example 1500ms from that point of time.
- Performance of getting monsters is bad.
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:
- is there a way to get this object on every this object and only this object change? I know there is function in puppeteer that can watch for DOM changes but is there something to watch global window object?
- can I do anything more to speed it up? I read about
worker_threads
in NodeJS, can it help getting rid of this micro lag or something else? Clustering?