diff --git a/console.ts b/console.ts deleted file mode 100644 index e7877a8..0000000 --- a/console.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as dyetty from "dyetty"; -import { createWriteStream, mkdirSync, existsSync } from "fs"; - -console.clear(); - -enum LogType { - INFO, - WARN, - ERROR -} - -enum LogTag { - INFO, - CHAT, - WARN, - ERROR -} - -const LogTags = [ - dyetty.bgGreen(dyetty.black(" INFO ")), - dyetty.bgCyan(dyetty.black(" CHAT ")), - dyetty.bgYellow(dyetty.black(" WARN ")), - dyetty.bgRed(" ERRR ") -] as const; - -const TagsForFile = [ - "[INFO]", - "[CHAT]", - "[WARN]", - "[ERRR]" -] as const; - -function correctValue(i:number) : string { - if (i <= 9) return `0${i}`; - else return i.toString(); -} - -function getTime() : string { - const time = new Date(); - return `[${correctValue(time.getHours())}:${correctValue(time.getMinutes())}:${correctValue(time.getSeconds())}]`; -} - -function log(tag:LogTag, log:string, logType:LogType = LogType.INFO) : void { - const stringTime = getTime(), - fileTag = TagsForFile[tag], - consoleTag = LogTags[tag]; - - Console.QUEUED_FOR_LOG += `${stringTime} ${fileTag} ${log}\n`; - switch (logType) { - case LogType.INFO: - return console.log(`${dyetty.green(stringTime)} ${consoleTag} ${log}`); - case LogType.WARN: - return console.warn(`${dyetty.green(stringTime)} ${consoleTag} ${log}`); - case LogType.ERROR: - return console.error(`${dyetty.green(stringTime)} ${consoleTag} ${log}`); - } -} - -// TODO: Keep old logs, rename on startup using file header? -if (!existsSync("./logs")) { - mkdirSync("./logs/"); -} - -export class Console { - public static QUEUED_FOR_LOG:string = ""; - private static logFileWriteStream = createWriteStream("./logs/latest.log"); - private static flushTimer:NodeJS.Timer = setInterval(() => { - if (Console.QUEUED_FOR_LOG.length !== 0) { - const strRef = Console.QUEUED_FOR_LOG; - Console.QUEUED_FOR_LOG = ""; - Console.logFileWriteStream.write(strRef); - } - }, 5000); - - public static printInfo(s:string) : void { - log(LogTag.INFO, s); - } - - public static printChat(s:string) : void { - log(LogTag.CHAT, s); - } - - public static printWarn(s:string) : void { - log(LogTag.WARN, s, LogType.WARN); - } - - public static printError(s:string) : void { - log(LogTag.ERROR, s, LogType.ERROR); - } -} \ No newline at end of file diff --git a/index.ts b/index.ts index 2989526..bb294d7 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,5 @@ import { Config } from "./config"; +import { Console } from "hsconsole"; import { readFileSync } from "fs"; import { MinecraftServer } from "./server/MinecraftServer"; import { SaveCompressionType } from "./server/enums/SaveCompressionType"; @@ -6,4 +7,6 @@ const tempConfig = JSON.parse(readFileSync("./config.json").toString()); tempConfig.saveCompression = SaveCompressionType[tempConfig.saveCompression]; const config:Config = tempConfig as Config; +Console.customHeader(`MC Beta Server started at ${new Date()}`); + new MinecraftServer(config); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e8db925..96a6887 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "bufferstuff": "^1.3.0", - "dyetty": "^1.0.1" + "hsconsole": "^1.0.2" }, "devDependencies": { "@types/node": "^20.4.5", @@ -1173,6 +1173,14 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/hsconsole": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hsconsole/-/hsconsole-1.0.2.tgz", + "integrity": "sha512-st+jaSpNw3uoIhE5vl2lVN8Op8yQF2FyLRdBG68s8vqjduJdKUGtoEXd8Zxe6du1zzpFHHRcU3zJbAq8BOmYQA==", + "dependencies": { + "dyetty": "^1.0.1" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", diff --git a/package.json b/package.json index a21d68b..8761629 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "homepage": "https://github.com/tgpholly/mc-beta-server#readme", "dependencies": { "bufferstuff": "^1.3.0", - "dyetty": "^1.0.1" + "hsconsole": "^1.0.2" }, "devDependencies": { "@types/node": "^20.4.5", diff --git a/server/MPClient.ts b/server/MPClient.ts index 99e4837..fcf926c 100644 --- a/server/MPClient.ts +++ b/server/MPClient.ts @@ -1,4 +1,4 @@ -import { Console } from "../console"; +import { Console } from "hsconsole"; import { IReader } from "bufferstuff"; import { MinecraftServer } from "./MinecraftServer"; import { Packet } from "./enums/Packet"; @@ -80,7 +80,7 @@ export class MPClient { Console.printInfo(packet.message = `Teleported ${this.entity.username} to ${message[1]} ${message[2]} ${message[3]}`); } else if (message[0] === "/csay") { const consoleMessage = `[CONSOLE] ${message.slice(1, message.length).join(" ")}`; - Console.printChat(consoleMessage); + Console.printInfo(`[CHAT] ${consoleMessage}`); this.mcServer.sendToAllClients(new PacketChat(consoleMessage).writeData()); } else if (message[0] === "/top") { // TODO: Figure out why this is broken @@ -98,7 +98,7 @@ export class MPClient { } packet.message = `<${this.entity.username}> ${packet.message}`; - Console.printChat(packet.message); + Console.printInfo(`[CHAT] ${packet.message}`); this.mcServer.sendToAllClients(packet.writeData()); } diff --git a/server/MinecraftServer.ts b/server/MinecraftServer.ts index 5687452..6c5c911 100644 --- a/server/MinecraftServer.ts +++ b/server/MinecraftServer.ts @@ -1,5 +1,5 @@ import { Config } from "../config"; -import { Console } from "../console"; +import { Console } from "hsconsole"; import { createReader, IReader, Endian } from "bufferstuff"; import { FunkyArray } from "../funkyArray"; import { Server, Socket } from "net"; @@ -17,6 +17,7 @@ import { Player } from "./entities/Player"; import { SaveCompressionType } from "./enums/SaveCompressionType"; import { WorldSaveManager } from "./WorldSaveManager"; import { World } from "./World"; +import { Chunk } from "./Chunk"; export class MinecraftServer { private static readonly PROTOCOL_VERSION = 14; @@ -51,6 +52,37 @@ export class MinecraftServer { public constructor(config:Config) { this.config = config; + process.on("SIGINT", async (signal) => { + Console.printInfo("Shutting down..."); + // Stop the server timer + clearInterval(this.serverClock); + // Disconnect all players + const kickPacket = new PacketDisconnectKick("Server shutting down.").writeData(); + this.sendToAllClients(kickPacket); + // Shut down the tcp server + this.server.close(); + // Save chunks + Console.printInfo("Saving worlds..."); + let savedWorldCount = 0; + let savedChunkCount = 0; + await this.worlds.forEach(async (world) => { + if (world.chunks.length !== 0) { + await world.chunks.forEach(async (chunk) => { + await world.unloadChunk(Chunk.CreateCoordPair(chunk.x, chunk.z)); + savedChunkCount++; + }); + } + savedWorldCount++; + }); + Console.printInfo(`Saved ${savedChunkCount} chunks from ${savedWorldCount} world(s).`); + + // Flush final console log to disk and close all writers + Console.cleanup(); + + // hsconsole is gone now so we have to use built in. + console.log("Goodbye"); + }); + if (this.config.saveCompression === SaveCompressionType.NONE) { Console.printWarn("=============- WARNING -============="); Console.printWarn(" Chunk compression is disabled. This"); diff --git a/tsconfig.json b/tsconfig.json index 21fddc5..19fb200 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,9 @@ "resolveJsonModule": true, "rootDir": "./", "outDir": "./build", - "strict": true + "strict": true, + "lib": [ + "ES2021.String" + ] } } \ No newline at end of file