more websocket stuff

This commit is contained in:
Lee 2024-01-01 19:43:19 +00:00
parent 15c0d2403f
commit 09f7b700ca
5 changed files with 85 additions and 44 deletions

@ -1,6 +1,6 @@
import SQLiteDatabase from "better-sqlite3"; import SQLiteDatabase from "better-sqlite3";
import cron from "node-cron"; import cron from "node-cron";
import Server, { PingResponse } from "../server/server"; import Server from "../server/server";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { getFormattedDate } from "../utils/timeUtils"; import { getFormattedDate } from "../utils/timeUtils";
@ -125,14 +125,13 @@ export default class Database {
* @param ip the IP address of the server * @param ip the IP address of the server
* @param playerCount the number of players online * @param playerCount the number of players online
*/ */
public insertPing(server: Server, response: PingResponse) { public insertPing(server: Server, response: Ping) {
const { timestamp, players } = response; const { timestamp, playerCount } = response;
const id = server.getID(); const id = server.getID();
const ip = server.getIP(); const ip = server.getIP();
const onlineCount = players.online;
const statement = this.db.prepare(INSERT_PING); 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 server the server to insert
* @param response the response 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) { public insertRecord(server: Server, response: Ping): boolean {
const { timestamp, players } = response; const { timestamp, playerCount } = response;
const id = server.getID(); const id = server.getID();
const ip = server.getIP(); 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); 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;
} }
} }

@ -15,12 +15,16 @@ export const database = new Database();
*/ */
export const serverManager = new ServerManager(); 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 // The scanner is responsible for scanning all servers
new Scanner(); new Scanner();
// The websocket server is responsible for // The websocket server is responsible for
// sending data to the client in real time // sending data to the client in real time
new WebsocketServer(Config.websocket.port);
// serverManager.getServers().forEach((server) => { // serverManager.getServers().forEach((server) => {
// const record = database.getRecord(server.getID()); // const record = database.getRecord(server.getID());

@ -1,6 +1,6 @@
import cron from "node-cron"; import cron from "node-cron";
import { database, serverManager } from ".."; import { database, serverManager, websocketServer } from "..";
import Server from "../server/server"; import Server, { ServerStatus } from "../server/server";
import Config from "../../data/config.json"; import Config from "../../data/config.json";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
@ -48,6 +48,7 @@ export default class Scanner {
online = true; online = true;
} catch (err) { } catch (err) {
logger.info(`Failed to ping ${server.getIP()}`, err); logger.info(`Failed to ping ${server.getIP()}`, err);
websocketServer.sendServerError(server, ServerStatus.OFFLINE);
return; return;
} }
@ -56,6 +57,9 @@ export default class Scanner {
} }
database.insertPing(server, response); 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);
} }
} }

@ -3,6 +3,7 @@ import { ResolvedServer, resolveDns } from "../utils/dnsResolver";
const bedrockPing = require("mcpe-ping-fixed"); // Doesn't have typescript definitions const bedrockPing = require("mcpe-ping-fixed"); // Doesn't have typescript definitions
import Config from "../../data/config.json"; import Config from "../../data/config.json";
import { Ping } from "../types/ping";
/** /**
* The type of server. * The type of server.
@ -11,18 +12,9 @@ import Config from "../../data/config.json";
*/ */
export type ServerType = "PC" | "PE"; export type ServerType = "PC" | "PE";
/** export enum ServerStatus {
* The response from a ping request to a server. OFFLINE = "Unable to reach host",
*/ }
export type PingResponse = {
timestamp: number;
ip: string;
version?: string;
players: {
online: number;
max?: number;
};
};
type ServerOptions = { type ServerOptions = {
id: number; id: number;
@ -90,7 +82,7 @@ export default class Server {
* @param server the server to ping * @param server the server to ping
* @returns the ping response or undefined if the server is offline * @returns the ping response or undefined if the server is offline
*/ */
public pingServer(server: Server): Promise<PingResponse | undefined> { public pingServer(server: Server): Promise<Ping | undefined> {
switch (server.getType()) { switch (server.getType()) {
case "PC": { case "PC": {
return this.pingPCServer(server); return this.pingPCServer(server);
@ -112,9 +104,7 @@ export default class Server {
* @param server the server to ping * @param server the server to ping
* @returns the ping response or undefined if the server is offline * @returns the ping response or undefined if the server is offline
*/ */
private async pingPCServer( private async pingPCServer(server: Server): Promise<Ping | undefined> {
server: Server
): Promise<PingResponse | undefined> {
if (this.dnsInfo.resolvedServer == undefined && !this.dnsInfo.hasResolved) { if (this.dnsInfo.resolvedServer == undefined && !this.dnsInfo.hasResolved) {
try { try {
const resolvedServer = await resolveDns(server.getIP()); const resolvedServer = await resolveDns(server.getIP());
@ -149,13 +139,10 @@ export default class Server {
this.favicon = res.favicon; // Set the favicon this.favicon = res.favicon; // Set the favicon
resolve({ resolve({
id: server.getID(),
timestamp: Date.now(), timestamp: Date.now(),
ip: ip, ip: ip,
version: res.version.name, playerCount: res.players.online,
players: {
online: res.players.online,
max: res.players.max,
},
}); });
}); });
}); });
@ -167,9 +154,7 @@ export default class Server {
* @param server the server to ping * @param server the server to ping
* @returns the ping response or undefined if the server is offline * @returns the ping response or undefined if the server is offline
*/ */
private async pingPEServer( private async pingPEServer(server: Server): Promise<Ping | undefined> {
server: Server
): Promise<PingResponse | undefined> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
bedrockPing( bedrockPing(
server.getIP(), server.getIP(),
@ -180,11 +165,10 @@ export default class Server {
} }
resolve({ resolve({
id: server.getID(),
timestamp: Date.now(), timestamp: Date.now(),
ip: server.getIP(), ip: server.getIP(),
players: { playerCount: res.currentPlayers,
online: res.currentPlayers,
},
}); });
} }
); );

@ -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"; import { logger } from "../utils/logger";
export default class WebsocketServer { export default class WebsocketServer {
private server: Server; private server: SocketServer;
constructor(port: number) { constructor(port: number) {
logger.info(`Starting websocket server on port ${port}`); logger.info(`Starting websocket server on port ${port}`);
this.server = new Server(port); this.server = new SocketServer(port);
this.server.on("connection", (socket) => { this.server.on("connection", (socket) => {
logger.info("ws: Client connected"); logger.debug("ws: Client connected");
// todo: send ping data to client // 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,
});
}
} }