Update Platforms #390

Merged
Sirherobrine23 merged 10 commits from StashCode into main 2022-06-09 20:22:38 +00:00
17 changed files with 384 additions and 266 deletions
Showing only changes of commit 2ebc90db9a - Show all commits

View File

@ -38,4 +38,4 @@ jobs:
run: npm ci run: npm ci
- name: Test - name: Test
run: npm run test:host -- --exit-on-error --show-return run: npm run test -- --exit-on-error --show-return

View File

@ -16,8 +16,8 @@
"scripts": { "scripts": {
"dev": "nodemon", "dev": "nodemon",
"build": "run-s build:*", "build": "run-s build:*",
"test:host": "node testProject.js", "test": "node testProject.js",
"test": "docker build . -f test.Dockerfile", "test:docker": "docker build . -f test.Dockerfile",
"build:cjs": "tsc --outDir dist/cjs --module commonjs", "build:cjs": "tsc --outDir dist/cjs --module commonjs",
"build:esm": "tsc --outDir dist/esm --module es6 && node -e 'const fs = require(\"fs\");const path = require(\"path\");const read = (pathRe) => {for (const fileFolde of fs.readdirSync(pathRe)) {const filePath = path.join(pathRe, fileFolde);if (fs.statSync(filePath).isDirectory()) read(filePath);else {console.log(filePath, \"-->\", filePath.replace(/\\.js$/, \".mjs\"));fs.renameSync(filePath, filePath.replace(/\\.js$/, \".mjs\"));}}};read(\"dist/esm\");'" "build:esm": "tsc --outDir dist/esm --module es6 && node -e 'const fs = require(\"fs\");const path = require(\"path\");const read = (pathRe) => {for (const fileFolde of fs.readdirSync(pathRe)) {const filePath = path.join(pathRe, fileFolde);if (fs.statSync(filePath).isDirectory()) read(filePath);else {console.log(filePath, \"-->\", filePath.replace(/\\.js$/, \".mjs\"));fs.renameSync(filePath, filePath.replace(/\\.js$/, \".mjs\"));}}};read(\"dist/esm\");'"
}, },

View File

@ -1,5 +1,5 @@
import * as bdsTypes from "./globalType"; import * as bdsTypes from "./globalType";
import * as platform from "./platform/index"; import platform from "./platform/index";
export default DownloadServer; export default DownloadServer;
export async function DownloadServer(Platform: bdsTypes.Platform, Version: string|boolean): Promise<{Version: string, Date: Date, url: string}> { export async function DownloadServer(Platform: bdsTypes.Platform, Version: string|boolean): Promise<{Version: string, Date: Date, url: string}> {

View File

@ -72,6 +72,7 @@ export async function startServer(): Promise<BdsSession> {
}); });
} }
}); });
// Player // Player
onLog.on("all", data => { onLog.on("all", data => {
if (/r\s+.*\:\s+.*\,\s+xuid\:\s+.*/gi.test(data)) { if (/r\s+.*\:\s+.*\,\s+xuid\:\s+.*/gi.test(data)) {

View File

@ -2,22 +2,29 @@ import * as bedrock from "./bedrock/index";
import * as pocketmine from "./pocketmine/index"; import * as pocketmine from "./pocketmine/index";
import * as java from "./java/index"; import * as java from "./java/index";
import * as spigot from "./spigot/index"; import * as spigot from "./spigot/index";
import { BdsSession } from "../globalType";
type globalPlatform = { type globalPlatform = {
[platform: string]: { [platform: string]: {
DownloadServer: (version: string) => Promise<{version: string, publishDate: Date, url: string}>, /**
* Download Server (and ¹auto install).
*
* 1: **In java server required java installation (if not installed, it will install it)**
*/
DownloadServer: (version: string|boolean) => Promise<{version: string, publishDate: Date, url: string}>,
server: {
/** get server session */
startServer: () => Promise<BdsSession>,
/** Get all Server Sessions */
getSessions: () => {[sessionId: string]: BdsSession}
},
backup: { backup: {
/** Create Platform Backup, and return Buffer zip File */
CreateBackup: () => Promise<Buffer>, CreateBackup: () => Promise<Buffer>,
} /** Restore Zip Backup, this option replace local files */
RestoreBackup: (buffer: Buffer) => Promise<void>,
},
} }
}; };
bedrock.backup.CreateBackup export default {bedrock, java, pocketmine, spigot} as globalPlatform;
const platforms: globalPlatform = {
bedrock,
pocketmine,
java,
spigot
}
export default platforms

View File

@ -0,0 +1,40 @@
import * as fsOld from "node:fs";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import admZip from "adm-zip";
import { serverRoot } from "../../pathControl";
const javaPath = path.join(serverRoot, "java");
const filesFoldertoIgnore = ["Server.jar", "eula.txt", "libraries", "logs", "usercache.json", "versions"];
/**
* Create backup for Worlds and Settings
*/
export async function CreateBackup(): Promise<Buffer> {
if (!(fsOld.existsSync(javaPath))) throw new Error("Install server");
const filesLint = (await fs.readdir(javaPath)).filter(file => !(filesFoldertoIgnore.some(folder => folder === file)));
const zip = new admZip();
for (const file of filesLint) {
const filePath = path.join(javaPath, file);
const stats = await fs.stat(filePath);
if (stats.isSymbolicLink()) {
const realPath = await fs.realpath(filePath);
const realStats = await fs.stat(realPath);
if (realStats.isDirectory()) zip.addLocalFolder(realPath, file);
else zip.addLocalFile(realPath, file);
} else if (stats.isDirectory()) zip.addLocalFolder(filePath);
else zip.addLocalFile(filePath);
}
return zip.toBuffer();
}
/**
* Restore backup for Worlds and Settings
*
* WARNING: This will overwrite existing files and World folder files
*/
export async function RestoreBackup(zipBuffer: Buffer): Promise<void> {
const zip = new admZip(zipBuffer);
await new Promise((resolve, reject) => zip.extractAllToAsync(javaPath, true, true, (err) => !!err ? reject(err) : resolve("")));
return;
}

View File

@ -1,3 +1,4 @@
import * as server from "./server"; import * as server from "./server";
import * as backup from "./backup";
import DownloadServer from "./download"; import DownloadServer from "./download";
export {server, DownloadServer}; export {server, DownloadServer, backup};

View File

@ -0,0 +1,40 @@
import * as fsOld from "node:fs";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import admZip from "adm-zip";
import { serverRoot } from '../../pathControl';
const javaPath = path.join(serverRoot, "pocketmine");
const filesFoldertoIgnore = [];
/**
* Create backup for Worlds and Settings
*/
export async function CreateBackup(): Promise<Buffer> {
if (!(fsOld.existsSync(javaPath))) throw new Error("Install server");
const filesLint = (await fs.readdir(javaPath)).filter(file => !(filesFoldertoIgnore.some(folder => folder === file)));
const zip = new admZip();
for (const file of filesLint) {
const filePath = path.join(javaPath, file);
const stats = await fs.stat(filePath);
if (stats.isSymbolicLink()) {
const realPath = await fs.realpath(filePath);
const realStats = await fs.stat(realPath);
if (realStats.isDirectory()) zip.addLocalFolder(realPath, file);
else zip.addLocalFile(realPath, file);
} else if (stats.isDirectory()) zip.addLocalFolder(filePath);
else zip.addLocalFile(filePath);
}
return zip.toBuffer();
}
/**
* Restore backup for Worlds and Settings
*
* WARNING: This will overwrite existing files and World folder files
*/
export async function RestoreBackup(zipBuffer: Buffer): Promise<void> {
const zip = new admZip(zipBuffer);
await new Promise((resolve, reject) => zip.extractAllToAsync(javaPath, true, true, (err) => !!err ? reject(err) : resolve("")));
return;
}

View File

@ -1,5 +1,6 @@
import * as addons from "./addons"; import * as addons from "./addons";
import * as config from "./config"; import * as config from "./config";
import * as server from "./server"; import * as server from "./server";
import * as backup from "./backup";
import DownloadServer from "./download"; import DownloadServer from "./download";
export {addons, config, server, DownloadServer}; export {addons, config, server, DownloadServer, backup};

View File

@ -12,25 +12,6 @@ import { createZipBackup } from "../../backup/zip";
const pocketmineSesions: {[key: string]: BdsSession} = {}; const pocketmineSesions: {[key: string]: BdsSession} = {};
export function getSessions() {return pocketmineSesions;} export function getSessions() {return pocketmineSesions;}
export function parseUserAction(data: string): {player: string; action: "connect"|"disconnect"|"unknown"; date: Date; xuid?: string;}|void {
if (/\[.*\]:\s+(.*)\s+(.*)\s+the\s+game/gi.test(data)) {
const actionDate = new Date();
const [action, player] = (data.match(/[.*]:\s+(.*)\s+(.*)\s+the\s+game/gi)||[]).slice(1, 3);
const __PlayerAction: {player: string, action: "connect"|"disconnect"|"unknown"} = {
player: player,
action: "unknown"
};
if (action === "joined") __PlayerAction.action = "connect";
else if (action === "left") __PlayerAction.action = "disconnect";
return {
player: __PlayerAction.player,
action: __PlayerAction.action,
date: actionDate
};
}
return;
}
const ServerPath = path.join(serverRoot, "pocketmine"); const ServerPath = path.join(serverRoot, "pocketmine");
export async function startServer(): Promise<BdsSession> { export async function startServer(): Promise<BdsSession> {
const SessionID = crypto.randomUUID(); const SessionID = crypto.randomUUID();
@ -71,16 +52,18 @@ export async function startServer(): Promise<BdsSession> {
} = {}; } = {};
const ports: Array<{port: number; protocol?: "TCP"|"UDP"; version?: "IPv4"|"IPv6"|"IPv4/IPv6";}> = []; const ports: Array<{port: number; protocol?: "TCP"|"UDP"; version?: "IPv4"|"IPv6"|"IPv4/IPv6";}> = [];
// Port // Port listen
onLog.on("all", data => { onLog.on("all", data => {
// [16:49:31.284] [Server thread/INFO]: Minecraft network interface running on [::]:19133 // [16:49:31.284] [Server thread/INFO]: Minecraft network interface running on [::]:19133
// [16:49:31.273] [Server thread/INFO]: Minecraft network interface running on 0.0.0.0:19132 // [16:49:31.273] [Server thread/INFO]: Minecraft network interface running on 0.0.0.0:19132
if (/\[.*\]:\s+Minecraft\s+network\s+interface\s+running\s+on\s+(.*)/gi.test(data)) { if (/\[.*\]:\s+Minecraft\s+network\s+interface\s+running\s+on\s+.*/gi.test(data)) {
const portParse = data.match(/\[.*\]:\s+Minecraft\s+network\s+interface\s+running\s+on\s+(.*)/)[1]; const matchString = data.match(/\[.*\]:\s+Minecraft\s+network\s+interface\s+running\s+on\s+(.*)/);
if (!!portParse) { if (!!matchString) {
if (/\[.*\]/.test(portParse)) { const portParse = matchString[1];
const isIpv6 = /\[.*\]/.test(portParse);
if (isIpv6) {
ports.push({ ports.push({
port: parseInt(portParse.match(/\[.*\]:\s+(.*)/)[1]), port: parseInt(portParse.replace(/\[.*\]:/, "").trim()),
version: "IPv6" version: "IPv6"
}); });
} else { } else {
@ -92,7 +75,8 @@ export async function startServer(): Promise<BdsSession> {
} }
} }
}); });
// Player
// Player Actions
onLog.on("all", data => { onLog.on("all", data => {
if (/\[.*\]:\s+(.*)\s+(.*)\s+the\s+game/gi.test(data)) { if (/\[.*\]:\s+(.*)\s+(.*)\s+the\s+game/gi.test(data)) {
const actionDate = new Date(); const actionDate = new Date();

View File

@ -0,0 +1,40 @@
import * as fsOld from "node:fs";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import admZip from "adm-zip";
import { serverRoot } from '../../pathControl';
const javaPath = path.join(serverRoot, "java");
const filesFoldertoIgnore = [];
/**
* Create backup for Worlds and Settings
*/
export async function CreateBackup(): Promise<Buffer> {
if (!(fsOld.existsSync(javaPath))) throw new Error("Install server");
const filesLint = (await fs.readdir(javaPath)).filter(file => !(filesFoldertoIgnore.some(folder => folder === file)));
const zip = new admZip();
for (const file of filesLint) {
const filePath = path.join(javaPath, file);
const stats = await fs.stat(filePath);
if (stats.isSymbolicLink()) {
const realPath = await fs.realpath(filePath);
const realStats = await fs.stat(realPath);
if (realStats.isDirectory()) zip.addLocalFolder(realPath, file);
else zip.addLocalFile(realPath, file);
} else if (stats.isDirectory()) zip.addLocalFolder(filePath);
else zip.addLocalFile(filePath);
}
return zip.toBuffer();
}
/**
* Restore backup for Worlds and Settings
*
* WARNING: This will overwrite existing files and World folder files
*/
export async function RestoreBackup(zipBuffer: Buffer): Promise<void> {
const zip = new admZip(zipBuffer);
await new Promise((resolve, reject) => zip.extractAllToAsync(javaPath, true, true, (err) => !!err ? reject(err) : resolve("")));
return;
}

View File

@ -1,2 +1,4 @@
import * as server from "./server";
import * as backup from "./backup";
import DownloadServer from "./download"; import DownloadServer from "./download";
export {DownloadServer}; export {server, backup, DownloadServer};

View File

@ -0,0 +1,154 @@
import path from "node:path";
import fs from "node:fs";
import events from "events";
import crypto from "crypto";
import node_cron from "cron";
import * as child_process from "../../childProcess";
import { backupRoot, serverRoot } from "../../pathControl";
import { BdsSession, bdsSessionCommands } from "../../globalType";
import { gitBackup, gitBackupOption } from "../../backup/git";
import { createZipBackup } from "../../backup/zip";
const javaSesions: {[key: string]: BdsSession} = {};
export function getSessions() {return javaSesions;}
const ServerPath = path.join(serverRoot, "spigot");
export async function startServer(): Promise<BdsSession> {
const SessionID = crypto.randomUUID();
// Start Server
const serverEvents = new events();
const StartDate = new Date();
const ServerProcess = await child_process.execServer({runOn: "host"}, "java", ["-jar", "Server.jar"], {cwd: ServerPath});
const { onExit } = ServerProcess;
const onLog = {on: ServerProcess.on, once: ServerProcess.once};
const playerCallbacks: {[id: string]: {callback: (data: {player: string; action: "connect"|"disconnect"|"unknown"; date: Date;}) => void}} = {};
const onPlayer = (callback: (data: {player: string; action: "connect"|"disconnect"|"unknown"; date: Date;}) => void) => {
const uid = crypto.randomUUID();
playerCallbacks[uid] = {callback: callback};
return uid;
};
const ports: Array<{port: number; protocol?: "TCP"|"UDP"; version?: "IPv4"|"IPv6"|"IPv4/IPv6";}> = [];
const playersConnections: {[player: string]: {action: "connect"|"disconnect"|"unknown", date: Date, history: Array<{action: "connect"|"disconnect"|"unknown", date: Date}>}} = {};
// Parse ports
onLog.on("all", data => {
const portParse = data.match(/Starting\s+Minecraft\s+server\s+on\s+(.*)\:(\d+)/);
if (!!portParse) {
ports.push({
port: parseInt(portParse[2]),
version: "IPv4/IPv6"
});
}
});
// Run Command
const serverCommands: bdsSessionCommands = {
/**
* Run any commands in server.
* @param command - Run any commands in server without parse commands
* @returns - Server commands
*/
execCommand: (...command) => {
ServerProcess.writelf(command.map(a => String(a)).join(" "));
return serverCommands;
},
tpPlayer: (player: string, x: number, y: number, z: number) => {
serverCommands.execCommand("tp", player, x, y, z);
return serverCommands;
},
worldGamemode: (gamemode: "survival"|"creative"|"hardcore") => {
serverCommands.execCommand("gamemode", gamemode);
return serverCommands;
},
userGamemode: (player: string, gamemode: "survival"|"creative"|"hardcore") => {
serverCommands.execCommand("gamemode", gamemode, player);
return serverCommands;
},
stop: (): Promise<number|null> => {
if (ServerProcess.Exec.exitCode !== null||ServerProcess.Exec.killed) return Promise.resolve(ServerProcess.Exec.exitCode);
if (ServerProcess.Exec.killed) return Promise.resolve(ServerProcess.Exec.exitCode);
ServerProcess.writelf("stop");
return ServerProcess.onExit();
}
}
const backupCron = (crontime: string|Date, option?: {type: "git"; config: gitBackupOption}|{type: "zip", config?: {pathZip?: string}}): node_cron.CronJob => {
// Validate Config
if (option) {
if (option.type === "git") {
if (!option.config) throw new Error("Config is required");
} else if (option.type === "zip") {}
else option = {type: "zip"};
}
async function lockServerBackup() {
serverCommands.execCommand("save hold");
await new Promise(accept => setTimeout(accept, 1000));
serverCommands.execCommand("save query");
await new Promise(accept => setTimeout(accept, 1000));
}
async function unLockServerBackup() {
serverCommands.execCommand("save resume");
await new Promise(accept => setTimeout(accept, 1000));
}
if (!option) option = {type: "zip"};
const CrontimeBackup = new node_cron.CronJob(crontime, async () => {
if (option.type === "git") {
await lockServerBackup();
await gitBackup(option.config).catch(() => undefined).then(() => unLockServerBackup());
} else if (option.type === "zip") {
await lockServerBackup();
if (!!option?.config?.pathZip) await createZipBackup({path: path.resolve(backupRoot, option?.config?.pathZip)}).catch(() => undefined);
else await createZipBackup(true).catch(() => undefined);
await unLockServerBackup();
}
});
CrontimeBackup.start();
onExit().catch(() => null).then(() => CrontimeBackup.stop());
return CrontimeBackup;
}
// Session log
const logFile = path.resolve(process.env.LOG_PATH||path.resolve(ServerPath, "../log"), `bedrock_${SessionID}.log`);
if(!(fs.existsSync(path.parse(logFile).dir))) fs.mkdirSync(path.parse(logFile).dir, {recursive: true});
const logStream = fs.createWriteStream(logFile, {flags: "w+"});
logStream.write(`[${StartDate.toString()}] Server started\n\n`);
ServerProcess.Exec.stdout.pipe(logStream);
ServerProcess.Exec.stderr.pipe(logStream);
const serverOn = (act: "started" | "ban", call: (...any: any[]) => void) => serverEvents.on(act, call);
const serverOnce = (act: "started" | "ban", call: (...any: any[]) => void) => serverEvents.once(act, call);
// Session Object
const Seesion: BdsSession = {
id: SessionID,
startDate: StartDate,
creteBackup: backupCron,
onExit: onExit,
onPlayer: onPlayer,
ports: () => ports,
getPlayer: () => playersConnections,
server: {
on: serverOn,
once: serverOnce
},
seed: undefined,
started: false,
addonManeger: undefined,
log: onLog,
commands: serverCommands,
};
onLog.on("all", lineData => {
// [22:35:26] [Server thread/INFO]: Done (6.249s)! For help, type "help"
if (/\[.*\].*\s+Done\s+\(.*\)\!.*/.test(lineData)) {
Seesion.started = true;
serverEvents.emit("started", new Date());
}
});
// Return Session
javaSesions[SessionID] = Seesion;
onExit().catch(() => null).then(() => delete javaSesions[SessionID]);
return Seesion;
}

View File

@ -1,16 +1,5 @@
import path from "node:path"; import platformManeger from "./platform";
import fs from "node:fs";
import events from "events";
import crypto from "crypto";
import node_cron from "cron";
import * as platformManeger from "./platform";
import * as child_process from "./childProcess";
import { parseConfig as serverConfigParse } from "./serverConfig";
import * as worldManeger from "./worldManeger";
import * as bdsTypes from "./globalType"; import * as bdsTypes from "./globalType";
import { backupRoot, serverRoot } from "./pathControl";
import { gitBackup, gitBackupOption } from "./backup/git";
import { createZipBackup } from "./backup/zip";
// Server Sessions // Server Sessions
const Sessions: {[Session: string]: bdsTypes.BdsSession} = {}; const Sessions: {[Session: string]: bdsTypes.BdsSession} = {};
@ -23,156 +12,9 @@ export function getSessions(): {[SessionID: string]: bdsTypes.BdsSession} {retur
// Start Server // Start Server
export default Start; export default Start;
export async function Start(Platform: bdsTypes.Platform, options?: bdsTypes.startServerOptions): Promise<bdsTypes.BdsSession> { export async function Start(Platform: bdsTypes.Platform, options?: bdsTypes.startServerOptions): Promise<bdsTypes.BdsSession> {
const SessionID = crypto.randomUUID();
const ServerPath = path.join(serverRoot, Platform);
if (!(fs.existsSync(ServerPath))) fs.mkdirSync(ServerPath, {recursive: true});
const Process: {command: string; args: Array<string>; env: {[env: string]: string};} = {
command: "",
args: [],
env: {}
};
if (Platform === "bedrock") return platformManeger.bedrock.server.startServer(); if (Platform === "bedrock") return platformManeger.bedrock.server.startServer();
else if (Platform === "java") return platformManeger.java.server.startServer(); else if (Platform === "java") return platformManeger.java.server.startServer();
else if (Platform === "pocketmine") return platformManeger.pocketmine.server.startServer(); else if (Platform === "pocketmine") return platformManeger.pocketmine.server.startServer();
else if (Platform === "spigot") { else if (Platform === "spigot") return platformManeger.spigot.server.startServer();
Process.command = "java"; throw new Error(`Platform ${Platform} is not supported`);
Process.args.push("-jar");
Process.args.push(path.resolve(ServerPath, "Spigot.jar"));
}
if (options?.storageOnlyWorlds) {
await worldManeger.storageWorld(Platform, ServerPath, (await serverConfigParse(Platform)).world);
}
// Start Server
const serverEvents = new events();
const StartDate = new Date();
const ServerProcess = await child_process.execServer({runOn: "host"}, Process.command, Process.args, {env: Process.env, cwd: ServerPath});
const { onExit } = ServerProcess;
const onLog = {on: ServerProcess.on, once: ServerProcess.once};
const playerCallbacks: {[id: string]: {callback: (data: {player: string; action: "connect"|"disconnect"|"unknown"; date: Date;}) => void}} = {};
const onPlayer = (callback: (data: {player: string; action: "connect"|"disconnect"|"unknown"; date: Date;}) => void) => {
const uid = crypto.randomUUID();
playerCallbacks[uid] = {callback: callback};
return uid;
};
const playersConnections: {
[player: string]: {
action: "connect"|"disconnect"|"unknown";
date: Date;
history: Array<{
action: "connect"|"disconnect"|"unknown";
date: Date
}>
}
} = {};
const ports: Array<{port: number; protocol?: "TCP"|"UDP"; version?: "IPv4"|"IPv6"|"IPv4/IPv6";}> = [];
// Run Command
const serverCommands: bdsTypes.bdsSessionCommands = {
/**
* Run any commands in server.
* @param command - Run any commands in server without parse commands
* @returns - Server commands
*/
execCommand: (...command) => {
ServerProcess.Exec.stdin.write(command.map(a => String(a)).join(" ")+"\n");
return serverCommands;
},
tpPlayer: (player: string, x: number, y: number, z: number) => {
serverCommands.execCommand("tp", player, x, y, z);
return serverCommands;
},
worldGamemode: (gamemode: "survival"|"creative"|"hardcore") => {
if (Platform === "spigot"||Platform === "pocketmine") serverCommands.execCommand("gamemode", gamemode);
return serverCommands;
},
userGamemode: (player: string, gamemode: "survival"|"creative"|"hardcore") => {
if (Platform === "spigot"||Platform === "pocketmine") serverCommands.execCommand("gamemode", gamemode, player);
return serverCommands;
},
stop: (): Promise<number|null> => {
if (ServerProcess.Exec.exitCode !== null||ServerProcess.Exec.killed) return Promise.resolve(ServerProcess.Exec.exitCode);
if (Platform === "spigot"||Platform === "pocketmine") serverCommands.execCommand("stop");
else ServerProcess.Exec.kill();
if (ServerProcess.Exec.killed) return Promise.resolve(ServerProcess.Exec.exitCode);
return ServerProcess.onExit();
}
}
const backupCron = (crontime: string|Date, option?: {type: "git"; config: gitBackupOption}|{type: "zip", config?: {pathZip?: string}}): node_cron.CronJob => {
// Validate Config
if (option) {
if (option.type === "git") {
if (!option.config) throw new Error("Config is required");
} else if (option.type === "zip") {}
else option = {type: "zip"};
}
async function lockServerBackup() {
if (Platform === "bedrock") {
serverCommands.execCommand("save hold");
await new Promise(accept => setTimeout(accept, 1000));
serverCommands.execCommand("save query");
await new Promise(accept => setTimeout(accept, 1000));
}
}
async function unLockServerBackup() {
if (Platform === "bedrock") {
serverCommands.execCommand("save resume");
await new Promise(accept => setTimeout(accept, 1000));
}
}
if (!option) option = {type: "zip"};
const CrontimeBackup = new node_cron.CronJob(crontime, async () => {
if (option.type === "git") {
await lockServerBackup();
await gitBackup(option.config).catch(() => undefined).then(() => unLockServerBackup());
} else if (option.type === "zip") {
await lockServerBackup();
if (!!option?.config?.pathZip) await createZipBackup({path: path.resolve(backupRoot, option?.config?.pathZip)}).catch(() => undefined);
else await createZipBackup(true).catch(() => undefined);
await unLockServerBackup();
}
});
CrontimeBackup.start();
onExit().catch(() => null).then(() => CrontimeBackup.stop());
return CrontimeBackup;
}
// Session log
const logFile = path.resolve(process.env.LOG_PATH||path.resolve(ServerPath, "../log"), `${Platform}_${SessionID}.log`);
if(!(fs.existsSync(path.parse(logFile).dir))) fs.mkdirSync(path.parse(logFile).dir, {recursive: true});
const logStream = fs.createWriteStream(logFile, {flags: "w+"});
logStream.write(`[${StartDate.toString()}] Server started\n\n`);
ServerProcess.Exec.stdout.pipe(logStream);
ServerProcess.Exec.stderr.pipe(logStream);
const serverOn = (act: "started" | "ban", call: (...any: any[]) => void) => serverEvents.on(act, call);
const serverOnce = (act: "started" | "ban", call: (...any: any[]) => void) => serverEvents.once(act, call);
// Session Object
const Seesion: bdsTypes.BdsSession = {
id: SessionID,
startDate: StartDate,
creteBackup: backupCron,
onExit: onExit,
onPlayer: onPlayer,
ports: () => ports,
getPlayer: () => playersConnections,
server: {
on: serverOn,
once: serverOnce
},
seed: undefined,
started: false,
addonManeger: undefined,
log: onLog,
commands: serverCommands,
};
// Return Session
Sessions[SessionID] = Seesion;
onExit().catch(() => null).then(() => delete Sessions[SessionID]);
return Seesion;
} }