2024-01-01 03:45:00 +00:00
|
|
|
import Database from "better-sqlite3";
|
|
|
|
import cron from "node-cron";
|
|
|
|
import { serverManager } from "..";
|
|
|
|
|
|
|
|
import Config from "../../data/config.json";
|
|
|
|
import Server, { PingResponse } from "../server/server";
|
|
|
|
|
|
|
|
const DATA_DIR = "data";
|
|
|
|
|
|
|
|
const PINGS_TABLE = "pings";
|
|
|
|
const RECORD_TABLE = "record";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SQL Queries
|
|
|
|
*/
|
2024-01-01 04:11:22 +00:00
|
|
|
const CREATE_PINGS_TABLE = `
|
|
|
|
CREATE TABLE IF NOT EXISTS pings (
|
|
|
|
id INTEGER NOT NULL,
|
|
|
|
timestamp BIGINT NOT NULL,
|
|
|
|
ip TINYTEXT NOT NULL,
|
|
|
|
player_count MEDIUMINT NOT NULL
|
|
|
|
);
|
|
|
|
`;
|
|
|
|
const CREATE_RECORD_TABLE = `
|
|
|
|
CREATE TABLE IF NOT EXISTS record (
|
|
|
|
id INTEGER PRIMARY KEY,
|
2024-01-01 03:45:00 +00:00
|
|
|
timestamp BIGINT NOT NULL,
|
|
|
|
ip TINYTEXT NOT NULL,
|
|
|
|
player_count MEDIUMINT NOT NULL
|
|
|
|
);
|
|
|
|
`;
|
|
|
|
|
2024-01-01 04:11:22 +00:00
|
|
|
const CREATE_PINGS_INDEX = `CREATE INDEX IF NOT EXISTS ip_index ON pings (id, ip, player_count)`;
|
|
|
|
const CREATE_TIMESTAMP_INDEX = `CREATE INDEX IF NOT EXISTS timestamp_index on PINGS (id, timestamp)`;
|
2024-01-01 03:45:00 +00:00
|
|
|
|
|
|
|
const INSERT_PING = `
|
2024-01-01 04:11:22 +00:00
|
|
|
INSERT INTO ${PINGS_TABLE} (id, timestamp, ip, player_count)
|
|
|
|
VALUES (?, ?, ?, ?)
|
2024-01-01 03:45:00 +00:00
|
|
|
`;
|
|
|
|
const INSERT_RECORD = `
|
2024-01-01 04:11:22 +00:00
|
|
|
INSERT INTO ${RECORD_TABLE} (id, timestamp, ip, player_count)
|
|
|
|
VALUES (?, ?, ?, ?)
|
|
|
|
ON CONFLICT(id) DO UPDATE SET
|
|
|
|
timestamp = excluded.timestamp,
|
|
|
|
player_count = excluded.player_count,
|
|
|
|
ip = excluded.ip
|
2024-01-01 03:45:00 +00:00
|
|
|
`;
|
|
|
|
|
|
|
|
export default class Scanner {
|
|
|
|
private db: Database.Database;
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
console.log("Loading scanner database");
|
|
|
|
this.db = new Database(`${DATA_DIR}/db.sqlite`);
|
|
|
|
|
|
|
|
console.log("Ensuring tables exist");
|
|
|
|
this.db.exec(CREATE_PINGS_TABLE); // Ensure the pings table exists
|
|
|
|
this.db.exec(CREATE_RECORD_TABLE); // Ensure the record table exists
|
|
|
|
|
|
|
|
console.log("Ensuring indexes exist");
|
|
|
|
this.db.exec(CREATE_PINGS_INDEX); // Ensure the pings index exists
|
|
|
|
this.db.exec(CREATE_TIMESTAMP_INDEX); // Ensure the timestamp index exists
|
|
|
|
|
|
|
|
console.log("Starting server scan");
|
|
|
|
cron.schedule(Config.scanner.updateCron, () => {
|
|
|
|
this.scanServers();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start a server scan to ping all servers.
|
|
|
|
*/
|
|
|
|
private async scanServers(): Promise<void> {
|
|
|
|
console.log(`Scanning servers ${serverManager.getServers().length}`);
|
|
|
|
|
|
|
|
// ping all servers in parallel
|
|
|
|
await Promise.all(
|
|
|
|
serverManager.getServers().map((server) => this.scanServer(server))
|
|
|
|
);
|
|
|
|
|
|
|
|
console.log("Finished scanning servers");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scans a server and inserts the ping into the database.
|
|
|
|
*
|
|
|
|
* @param server the server to scan
|
|
|
|
* @returns a promise that resolves when the server has been scanned
|
|
|
|
*/
|
|
|
|
async scanServer(server: Server): Promise<void> {
|
|
|
|
//console.log(`Scanning server ${server.getIP()} - ${server.getType()}`);
|
|
|
|
let response;
|
|
|
|
let online = false;
|
|
|
|
|
|
|
|
try {
|
|
|
|
response = await server.pingServer(server);
|
|
|
|
if (response == undefined) {
|
|
|
|
return; // Server is offline
|
|
|
|
}
|
|
|
|
online = true;
|
|
|
|
} catch (err) {
|
|
|
|
console.log(`Failed to ping ${server.getIP()}`, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!online || !response) {
|
|
|
|
return; // Server is offline
|
|
|
|
}
|
|
|
|
|
2024-01-01 04:11:22 +00:00
|
|
|
this.insertPing(server, response);
|
2024-01-01 03:45:00 +00:00
|
|
|
this.insertRecord(server, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a ping into the database.
|
|
|
|
*
|
|
|
|
* @param timestamp the timestamp of the ping
|
|
|
|
* @param ip the IP address of the server
|
|
|
|
* @param playerCount the number of players online
|
|
|
|
*/
|
2024-01-01 04:11:22 +00:00
|
|
|
private insertPing(server: Server, response: PingResponse): void {
|
|
|
|
const { timestamp, players } = response;
|
|
|
|
const id = server.getID();
|
|
|
|
const ip = server.getIP();
|
|
|
|
const onlineCount = players.online;
|
|
|
|
|
2024-01-01 03:45:00 +00:00
|
|
|
const statement = this.db.prepare(INSERT_PING);
|
2024-01-01 04:11:22 +00:00
|
|
|
statement.run(id, timestamp, ip, onlineCount); // Insert the ping into the database
|
2024-01-01 03:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a record into the database.
|
|
|
|
*
|
|
|
|
* @param server the server to insert
|
|
|
|
* @param response the response to insert
|
|
|
|
*/
|
|
|
|
private insertRecord(server: Server, response: PingResponse): void {
|
|
|
|
const { timestamp, players } = response;
|
2024-01-01 04:11:22 +00:00
|
|
|
const id = server.getID();
|
2024-01-01 03:45:00 +00:00
|
|
|
const ip = server.getIP();
|
|
|
|
const onlineCount = players.online;
|
|
|
|
|
|
|
|
const statement = this.db.prepare(INSERT_RECORD);
|
2024-01-01 04:11:22 +00:00
|
|
|
statement.run(id, timestamp, ip, onlineCount); // Insert the record into the database
|
2024-01-01 03:45:00 +00:00
|
|
|
}
|
|
|
|
}
|