diff --git a/src/database/database.ts b/src/database/database.ts index eb395ba..24580d2 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,6 +1,6 @@ import SQLiteDatabase from "better-sqlite3"; import cron from "node-cron"; -import Server, { PingResponse } from "../server/server"; +import Server from "../server/server"; import { logger } from "../utils/logger"; import { getFormattedDate } from "../utils/timeUtils"; @@ -125,14 +125,13 @@ export default class Database { * @param ip the IP address of the server * @param playerCount the number of players online */ - public insertPing(server: Server, response: PingResponse) { - const { timestamp, players } = response; + public insertPing(server: Server, response: Ping) { + const { timestamp, playerCount } = response; const id = server.getID(); const ip = server.getIP(); - const onlineCount = players.online; const statement = this.db.prepare(INSERT_PING); - statement.run(id, timestamp, ip, onlineCount); // Insert the ping into the database + statement.run(id, timestamp, ip, playerCount); // Insert the ping into the database } /** @@ -140,14 +139,20 @@ export default class Database { * * @param server the server to insert * @param response the response to insert + * @returns true if the a new record was set, false otherwise */ - public insertRecord(server: Server, response: PingResponse) { - const { timestamp, players } = response; + public insertRecord(server: Server, response: Ping): boolean { + const { timestamp, playerCount } = response; const id = server.getID(); const ip = server.getIP(); - const onlineCount = players.online; + + const oldRecord = this.getRecord(id); + if (oldRecord && oldRecord.playerCount >= playerCount) { + return false; // Don't update the record if the player count is lower + } const statement = this.db.prepare(INSERT_RECORD); - statement.run(id, timestamp, ip, onlineCount); // Insert the record into the database + statement.run(id, timestamp, ip, playerCount); // Insert the record into the database + return true; } } diff --git a/src/index.ts b/src/index.ts index 0db0c3b..fc2f4ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,12 +15,16 @@ export const database = new Database(); */ export const serverManager = new ServerManager(); +/** + * The websocket server instance. + */ +export const websocketServer = new WebsocketServer(Config.websocket.port); + // The scanner is responsible for scanning all servers new Scanner(); // The websocket server is responsible for // sending data to the client in real time -new WebsocketServer(Config.websocket.port); // serverManager.getServers().forEach((server) => { // const record = database.getRecord(server.getID()); diff --git a/src/scanner/scanner.ts b/src/scanner/scanner.ts index 047ea16..52d39a8 100644 --- a/src/scanner/scanner.ts +++ b/src/scanner/scanner.ts @@ -1,6 +1,6 @@ import cron from "node-cron"; -import { database, serverManager } from ".."; -import Server from "../server/server"; +import { database, serverManager, websocketServer } from ".."; +import Server, { ServerStatus } from "../server/server"; import Config from "../../data/config.json"; import { logger } from "../utils/logger"; @@ -48,6 +48,7 @@ export default class Scanner { online = true; } catch (err) { logger.info(`Failed to ping ${server.getIP()}`, err); + websocketServer.sendServerError(server, ServerStatus.OFFLINE); return; } @@ -56,6 +57,9 @@ export default class Scanner { } database.insertPing(server, response); - database.insertRecord(server, response); + const isNewRecord = database.insertRecord(server, response); + + // todo: send all server pings at once + websocketServer.sendNewPing(server, response, isNewRecord); } } diff --git a/src/server/server.ts b/src/server/server.ts index d144a46..2051a4e 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -3,6 +3,7 @@ import { ResolvedServer, resolveDns } from "../utils/dnsResolver"; const bedrockPing = require("mcpe-ping-fixed"); // Doesn't have typescript definitions import Config from "../../data/config.json"; +import { Ping } from "../types/ping"; /** * The type of server. @@ -11,18 +12,9 @@ import Config from "../../data/config.json"; */ export type ServerType = "PC" | "PE"; -/** - * The response from a ping request to a server. - */ -export type PingResponse = { - timestamp: number; - ip: string; - version?: string; - players: { - online: number; - max?: number; - }; -}; +export enum ServerStatus { + OFFLINE = "Unable to reach host", +} type ServerOptions = { id: number; @@ -90,7 +82,7 @@ export default class Server { * @param server the server to ping * @returns the ping response or undefined if the server is offline */ - public pingServer(server: Server): Promise { + public pingServer(server: Server): Promise { switch (server.getType()) { case "PC": { return this.pingPCServer(server); @@ -112,9 +104,7 @@ export default class Server { * @param server the server to ping * @returns the ping response or undefined if the server is offline */ - private async pingPCServer( - server: Server - ): Promise { + private async pingPCServer(server: Server): Promise { if (this.dnsInfo.resolvedServer == undefined && !this.dnsInfo.hasResolved) { try { const resolvedServer = await resolveDns(server.getIP()); @@ -149,13 +139,10 @@ export default class Server { this.favicon = res.favicon; // Set the favicon resolve({ + id: server.getID(), timestamp: Date.now(), ip: ip, - version: res.version.name, - players: { - online: res.players.online, - max: res.players.max, - }, + playerCount: res.players.online, }); }); }); @@ -167,9 +154,7 @@ export default class Server { * @param server the server to ping * @returns the ping response or undefined if the server is offline */ - private async pingPEServer( - server: Server - ): Promise { + private async pingPEServer(server: Server): Promise { return new Promise((resolve, reject) => { bedrockPing( server.getIP(), @@ -180,11 +165,10 @@ export default class Server { } resolve({ + id: server.getID(), timestamp: Date.now(), ip: server.getIP(), - players: { - online: res.currentPlayers, - }, + playerCount: res.currentPlayers, }); } ); diff --git a/src/websocket/websocket.ts b/src/websocket/websocket.ts index 56ebbbc..d0491f0 100644 --- a/src/websocket/websocket.ts +++ b/src/websocket/websocket.ts @@ -1,17 +1,61 @@ -import { Server } from "socket.io"; +import { Server as SocketServer } from "socket.io"; +import Server, { ServerStatus } from "../server/server"; +import { Ping } from "../types/ping"; import { logger } from "../utils/logger"; export default class WebsocketServer { - private server: Server; + private server: SocketServer; constructor(port: number) { logger.info(`Starting websocket server on port ${port}`); - this.server = new Server(port); + this.server = new SocketServer(port); this.server.on("connection", (socket) => { - logger.info("ws: Client connected"); + logger.debug("ws: Client connected"); // todo: send ping data to client }); } + + /** + * Sends the latest ping data for the given server to all clients. + * + * @param server the server to send the ping for + * @param pingResponse the ping data to send + * @param isNewRecord whether a new record has been set + */ + public sendNewPing( + server: Server, + pingResponse: Ping, + isNewRecord: boolean + ): void { + logger.debug(`ws: Sending new ping for ${server.getName()}`); + this.server.emit("newPing", { + server: server.getID(), + timestamp: pingResponse.timestamp, + playerCount: pingResponse.playerCount, + }); + if (isNewRecord) { + logger.debug(`ws: Sending new record for ${server.getName()}`); + this.server.emit("newRecord", { + server: server.getID(), + timestamp: pingResponse.timestamp, + playerCount: pingResponse.playerCount, + }); + } + } + + /** + * Sends the server status for the given server to all clients. + * + * @param server the server to send the status for + * @param status the status to send + */ + public sendServerError(server: Server, status: ServerStatus): void { + logger.debug(`ws: Sending server status for ${server.getName()}`); + this.server.emit("serverStatus", { + server: server.getID(), + status: status, + }); + } }