Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 77 additions & 42 deletions src/process/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as Natives from './native/index.js';
const Native = Natives[process.platform];


const timestamps = {}, names = {}, pids = {};
const timestamps = {}, names = {}, pids = {}, cache = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know most of these already existed before this PR, but using plain objects for this is bad. When you have frequent writes / deletes, Maps perform way better, both in terms of speed and memory usage. Changing all of these to Maps might also make it perform better

export default class ProcessServer {
constructor(handlers) {
if (!Native) return; // log('unsupported platform:', process.platform);
Expand All @@ -31,57 +31,75 @@ export default class ProcessServer {
// const startTime = performance.now();
const processes = await Native.getProcesses();
const ids = [];
const cacheKeys = {};

// log(`got processed in ${(performance.now() - startTime).toFixed(2)}ms`);

for (const [ pid, _path, args ] of processes) {
const path = _path.toLowerCase().replaceAll('\\', '/');
const toCompare = [];
const splitPath = path.split('/');
for (let i = 1; i < splitPath.length; i++) {
toCompare.push(splitPath.slice(-i).join('/'));

// the cache key includes every dependency of the DetectableDB scan
const cacheKey = `${path}\0${args}`;
cacheKeys[cacheKey] = true;

var detected = [];
if (cache[cacheKey] !== undefined) {
detected = cache[cacheKey];
}
else {
const toCompare = [];
const splitPath = path.split('/');
for (let i = 1; i < splitPath.length; i++) {
toCompare.push(splitPath.slice(-i).join('/'));
}

for (const p of toCompare.slice()) { // add more possible tweaked paths for less false negatives
toCompare.push(p.replace('64', '')); // remove 64bit identifiers-ish
toCompare.push(p.replace('.x64', ''));
toCompare.push(p.replace('x64', ''));
toCompare.push(p.replace('_64', ''));
for (const p of toCompare.slice()) { // add more possible tweaked paths for less false negatives
toCompare.push(p.replace('64', '')); // remove 64bit identifiers-ish
toCompare.push(p.replace('.x64', ''));
toCompare.push(p.replace('x64', ''));
toCompare.push(p.replace('_64', ''));
}

for (const { executables, id, name } of DetectableDB) {
if (executables?.some(x => {
if (x.is_launcher) return false;
if (x.name[0] === '>' ? x.name.substring(1) !== toCompare[0] : !toCompare.some(y => x.name === y)) return false;
if (args && x.arguments) return args.join(" ").indexOf(x.arguments) > -1;
return true;
})) {
detected.push({ executables, id, name });
}
}

cache[cacheKey] = detected;
}

for (const { executables, id, name } of detected) {
names[id] = name;
pids[id] = pid;

ids.push(id);
if (!timestamps[id]) {
log('detected game!', name);
timestamps[id] = Date.now();
}

for (const { executables, id, name } of DetectableDB) {
if (executables?.some(x => {
if (x.is_launcher) return false;
if (x.name[0] === '>' ? x.name.substring(1) !== toCompare[0] : !toCompare.some(y => x.name === y)) return false;
if (args && x.arguments) return args.join(" ").indexOf(x.arguments) > -1;
return true;
})) {
names[id] = name;
pids[id] = pid;

ids.push(id);
if (!timestamps[id]) {
log('detected game!', name);
timestamps[id] = Date.now();
// Resending this on evry scan is intentional, so that in the case that arRPC scans processes before Discord, existing activities will be sent
this.handlers.message({
socketId: id
}, {
cmd: 'SET_ACTIVITY',
args: {
activity: {
application_id: id,
name,
timestamps: {
start: timestamps[id]
}
},
pid
}

// Resending this on evry scan is intentional, so that in the case that arRPC scans processes before Discord, existing activities will be sent
this.handlers.message({
socketId: id
}, {
cmd: 'SET_ACTIVITY',
args: {
activity: {
application_id: id,
name,
timestamps: {
start: timestamps[id]
}
},
pid
}
});
}
});
}
}

Expand All @@ -102,6 +120,23 @@ export default class ProcessServer {
}
}

const globalCacheKeys = Object.keys(cache);
const currentCacheKeys = Object.keys(cacheKeys);
// log(`gc check: ${globalCacheKeys.length} > ${currentCacheKeys.length * 2.0}`);
if (globalCacheKeys.length > currentCacheKeys.length * 2.0) {
const beforeCount = Object.keys(cache).length;

for (const key in currentCacheKeys) {
delete globalCacheKeys[key];
}

for (const i in globalCacheKeys) {
delete cache[globalCacheKeys[i]];
}

log(`cache gc complete: ${beforeCount} -> ${Object.keys(cache).length}`);
}

// log(`finished scan in ${(performance.now() - startTime).toFixed(2)}ms`);
// process.stdout.write(`\r${' '.repeat(100)}\r[${rgb(88, 101, 242, 'arRPC')} > ${rgb(237, 66, 69, 'process')}] scanned (took ${(performance.now() - startTime).toFixed(2)}ms)`);
}
Expand Down