Core apis #528
@@ -1 +1,2 @@
|
|||||||
export * as listVersion from "./listVersion.js";
|
export * as listVersion from "./listVersion.js";
|
||||||
|
export * from "./main.js";
|
@@ -1,20 +1,27 @@
|
|||||||
import { http, Github } from "@sirherobrine23/http";
|
import { http, Github } from "@sirherobrine23/http";
|
||||||
|
import { versionsStorages } from "../../serverRun.js";
|
||||||
import util from "node:util";
|
import util from "node:util";
|
||||||
import xml from "xml-js";
|
import xml from "xml-js";
|
||||||
|
|
||||||
export interface mojangInfo {
|
export interface mojangInfo {
|
||||||
releaseDate: Date;
|
date: Date,
|
||||||
release: "oficial" | "beta";
|
release?: "oficial" | "preview",
|
||||||
files: { [P in NodeJS.Platform]?: { [A in NodeJS.Architecture]?: string } }
|
url: {
|
||||||
|
[platform in NodeJS.Platform]?: {
|
||||||
|
[arch in NodeJS.Architecture]?: {
|
||||||
|
[ext in "tgz" | "zip"]?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mojangCache = new Map<string, mojangInfo>();
|
export const mojangCache = new versionsStorages<mojangInfo>();
|
||||||
export async function listMojang() {
|
export async function listMojang() {
|
||||||
const versions = await http.jsonRequestBody<{ version: string, release?: "stable" | "preview", date: string, url: { [P in NodeJS.Platform]?: { [A in NodeJS.Architecture]?: string } } }[]>("https://raw.githubusercontent.com/Sirherobrine23/BedrockFetch/main/versions/all.json");
|
const versions = await http.jsonRequestBody<({version: string} & mojangInfo)[]>("https://raw.githubusercontent.com/Sirherobrine23/BedrockFetch/main/versions/all.json");
|
||||||
versions.forEach(rel => mojangCache.has(rel.version) ? null : mojangCache.set(rel.version, {
|
versions.filter(ver => !(mojangCache.has(ver.version))).forEach(rel => mojangCache.set(rel.version, {
|
||||||
releaseDate: new Date(rel.release),
|
date: new Date(rel.date),
|
||||||
release: !rel.release ? "oficial" : rel.release === "preview" ? "beta" : "oficial",
|
release: rel.release,
|
||||||
files: rel.url
|
url: rel.url
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +41,7 @@ export interface powernukkitDownload {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const powernukkitCache = new Map<string, powernukkitDownload>();
|
export const powernukkitCache = new versionsStorages<powernukkitDownload>();
|
||||||
export async function listPowernukkitProject() {
|
export async function listPowernukkitProject() {
|
||||||
const releases = await http.jsonRequestBody<{[k in "releases"|"snapshots"]: powerNukkitRelease[]}>("https://raw.githubusercontent.com/PowerNukkit/powernukkit-version-aggregator/master/powernukkit-versions.json");
|
const releases = await http.jsonRequestBody<{[k in "releases"|"snapshots"]: powerNukkitRelease[]}>("https://raw.githubusercontent.com/PowerNukkit/powernukkit-version-aggregator/master/powernukkit-versions.json");
|
||||||
const releasesData = (Object.keys(releases) as (keyof typeof releases)[]).map(releaseType => releases[releaseType].map(data => ({...data, releaseType}))).flat(1).sort((b, a) => Math.min(1, Math.max(-1, a.releaseTime - b.releaseTime)));
|
const releasesData = (Object.keys(releases) as (keyof typeof releases)[]).map(releaseType => releases[releaseType].map(data => ({...data, releaseType}))).flat(1).sort((b, a) => Math.min(1, Math.max(-1, a.releaseTime - b.releaseTime)));
|
||||||
@@ -70,8 +77,8 @@ export interface cloudburstDownload {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const nukkitCache = new Map<string, cloudburstDownload>();
|
export const nukkitCache = new versionsStorages<cloudburstDownload>();
|
||||||
export const cloudburstCache = new Map<string, cloudburstDownload>();
|
export const cloudburstCache = new versionsStorages<cloudburstDownload>();
|
||||||
export async function listCloudburstProject() {
|
export async function listCloudburstProject() {
|
||||||
const Projects = [ "Nukkit", "Server" ] as const;
|
const Projects = [ "Nukkit", "Server" ] as const;
|
||||||
for (const Project of Projects) {
|
for (const Project of Projects) {
|
||||||
@@ -142,7 +149,7 @@ export interface pocketmineDownload {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const pocketmineCache = new Map<string, pocketmineDownload>();
|
export const pocketmineCache = new versionsStorages<pocketmineDownload>();
|
||||||
const pocketmineGithub = await Github.repositoryManeger("pmmp", "PocketMine-MP");
|
const pocketmineGithub = await Github.repositoryManeger("pmmp", "PocketMine-MP");
|
||||||
export async function listPocketmineProject() {
|
export async function listPocketmineProject() {
|
||||||
const pocketmineReleases = (await pocketmineGithub.release.getRelease()).filter(rel => (rel.assets.find(assert => assert.name.endsWith(".phar")) ?? {}).browser_download_url);
|
const pocketmineReleases = (await pocketmineGithub.release.getRelease()).filter(rel => (rel.assets.find(assert => assert.name.endsWith(".phar")) ?? {}).browser_download_url);
|
||||||
|
@@ -1,19 +1,210 @@
|
|||||||
import { getCacheVersions } from "./listVersion.js";
|
import { extendsFS } from "@sirherobrine23/extends";
|
||||||
import { serverManeger } from "../../serverRun.js";
|
import { http } from "@sirherobrine23/http";
|
||||||
import semver from "semver";
|
import AdmZip from "adm-zip";
|
||||||
|
import child_process from "node:child_process";
|
||||||
|
import { createWriteStream } from "node:fs";
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import readline from "node:readline";
|
||||||
|
import { finished } from "node:stream/promises";
|
||||||
|
import tar from "tar";
|
||||||
|
import { bdsFilesBucket } from "../../internalClouds.js";
|
||||||
|
import { customEvent, defineEvents } from "../../serverRun.js";
|
||||||
|
import * as bedrockVersions from "./listVersion.js";
|
||||||
|
|
||||||
export type platforms = "mojang"|"pocketmine"|"powernukkit"|"nukkit"|"cloudburst";
|
export type platforms = "mojang" | "pocketmine" | "powernukkit" | "nukkit" | "cloudburst";
|
||||||
export async function installServer({platform, version}: {platform?: platforms, version?: string} = {}) {
|
|
||||||
if (!platform) platform = "mojang";
|
export interface bedrockPorts { }
|
||||||
else if ((!(["mojang", "pocketmine", "powernukkit", "nukkit", "cloudburst"]).includes(platform))) throw new Error("Invalid platform");
|
|
||||||
const versions = getCacheVersions();
|
export interface playerInfo {
|
||||||
const getLatest = (keys: IterableIterator<string>|string[]) => Array.from(keys).sort((b, a) => semver.compare(semver.valid(semver.coerce(a)), semver.valid(semver.coerce(b)))).at(0);
|
connected: boolean;
|
||||||
if (!version) version = "latest";
|
banned: boolean;
|
||||||
if (platform === "mojang") {
|
historic: {
|
||||||
if (version === "latest") version = getLatest(Object.keys(versions.mojang));
|
action: "connected" | "spawned" | "disconnected";
|
||||||
const release = versions.mojang[version];
|
actionDate: Date;
|
||||||
if (!release) throw new Error("Not valid Release");
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
class playerListen extends Map<string, playerInfo> {
|
||||||
|
constructor() { super(); }
|
||||||
|
toJSON() {
|
||||||
|
return Array.from(this.keys()).reduce<{ [playerName: string]: playerInfo }>((acc, player) => {
|
||||||
|
acc[player] = this.get(player);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState(playerName: string, state: playerInfo["historic"][number]["action"]) {
|
||||||
|
const actionDate = new Date();
|
||||||
|
if (!(this.has(playerName))) throw new Error("Set Player");
|
||||||
|
const playerData = super.get(playerName);
|
||||||
|
if (state === "disconnected") playerData.connected = false; else playerData.connected = true;
|
||||||
|
playerData.historic.push({ action: state, actionDate });
|
||||||
|
super.set(playerName, playerData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runServer() {}
|
/**
|
||||||
|
* Return boolean if Class input is Bedrock class server
|
||||||
|
* @param event - Bedrock class
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isBedrock(event: Bedrock<any>): event is Bedrock<any> {
|
||||||
|
return event instanceof Bedrock;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type bedrockEvents = defineEvents<{
|
||||||
|
logLine(lineString: string): void;
|
||||||
|
portListen(info: bedrockPorts): void
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
|
||||||
|
readonly serverFolder: string;
|
||||||
|
readonly rootServer: string;
|
||||||
|
readonly platform: P;
|
||||||
|
constructor(rootServer: string, platform: P) {
|
||||||
|
super();
|
||||||
|
this.platform = platform;
|
||||||
|
this.rootServer = rootServer;
|
||||||
|
this.serverFolder = path.join(rootServer, "server");
|
||||||
|
Object.defineProperty(this, "rootServer", { writable: false });
|
||||||
|
Object.defineProperty(this, "serverFolder", { writable: false });
|
||||||
|
if ((!(["mojang", "pocketmine", "powernukkit", "nukkit", "cloudburst"]).includes(platform))) throw new Error("Invalid platform");
|
||||||
|
Object.defineProperty(this, "platform", { writable: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
async installServer(version: string | number) {
|
||||||
|
const { platform } = this;
|
||||||
|
if (!(await extendsFS.exists(this.serverFolder))) await fs.mkdir(this.serverFolder, { recursive: true });
|
||||||
|
if (platform === "mojang") {
|
||||||
|
const release = bedrockVersions.mojangCache.get(version);
|
||||||
|
if (!release) throw new Error("Not valid Release");
|
||||||
|
const serverURL = release.url[process.platform]?.[process.arch]?.tgz;
|
||||||
|
if (!serverURL) throw new Error("Current platform not support mojang server");
|
||||||
|
let backupFiles = [{ file: "allowlist.json", data: "" }, { file: "permissions.json", data: "" }, { file: "server.properties", data: "" }];
|
||||||
|
backupFiles = await Promise.all(backupFiles.map(async file => { file.data = await fs.readFile(path.join(this.serverFolder, file.file), "utf8").catch(() => ""); return file }));
|
||||||
|
await finished((await http.streamRequest(serverURL)).pipe(tar.extract({ cwd: this.serverFolder, preserveOwner: true, keep: false })));
|
||||||
|
await Promise.all(backupFiles.filter(file => !!(file.data.trim())).map(async file => fs.writeFile(path.join(this.serverFolder, file.file), file.data)));
|
||||||
|
} else if (platform === "pocketmine") {
|
||||||
|
const release = bedrockVersions.pocketmineCache.get(version);
|
||||||
|
if (!release) throw new Error("Not valid Release");
|
||||||
|
await finished(await http.streamRequest(release.url), createWriteStream(path.join(this.serverFolder, "server.phar")));
|
||||||
|
let phpFiles = (await bdsFilesBucket.listFiles("php_bin/")).map(file => ({ name: file.name.slice(8).toLowerCase(), data: file })).filter(file => file.name.startsWith(`${process.platform}_${process.arch}`));
|
||||||
|
if (!phpFiles.length) throw new Error("Cannot get php binary to current platform");
|
||||||
|
const phpFile = phpFiles.sort((b, a) => b.data.Dates.Modified.getTime() - a.data.Dates.Modified.getTime()).at(0);
|
||||||
|
await fs.rm(path.join(this.serverFolder, "bin"), { recursive: true, force: true });
|
||||||
|
if (phpFile.name.endsWith(".tar.gz") || phpFile.name.endsWith(".tgz")) await finished((await phpFile.data.getFile()).pipe(tar.extract({ cwd: path.join(this.serverFolder, "bin") })));
|
||||||
|
else {
|
||||||
|
const tmpFile = path.join(tmpdir(), Date.now() + "_" + phpFile.name);
|
||||||
|
await finished((await phpFile.data.getFile()).pipe(createWriteStream(tmpFile)));
|
||||||
|
await new Promise<void>((done, reject) => (new AdmZip(tmpFile)).extractAllToAsync(path.join(this.serverFolder, "bin"), true, true, err => err ? reject(err) : done()));
|
||||||
|
await fs.rm(tmpFile, { force: true });
|
||||||
|
}
|
||||||
|
} else if (platform === "powernukkit") {
|
||||||
|
const release = bedrockVersions.powernukkitCache.get(version);
|
||||||
|
if (!release) throw new Error("Not valid Release");
|
||||||
|
await finished(await http.streamRequest(release.url), createWriteStream(path.join(this.serverFolder, "server.jar")));
|
||||||
|
} else if (platform === "cloudburst" || platform === "nukkit") {
|
||||||
|
const platformVersions = platform === "cloudburst" ? bedrockVersions.cloudburstCache : bedrockVersions.nukkitCache;
|
||||||
|
const release = platformVersions.get(version);
|
||||||
|
if (!release) throw new Error("Not valid Release");
|
||||||
|
await finished(await http.streamRequest(release.url), createWriteStream(path.join(this.serverFolder, "server.jar")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ports: bedrockPorts[] = [];
|
||||||
|
readonly players = new playerListen();
|
||||||
|
|
||||||
|
serverProcess?: child_process.ChildProcess;
|
||||||
|
async runServer() {
|
||||||
|
const { platform } = this;
|
||||||
|
if (platform === "nukkit" || platform === "powernukkit" || platform === "cloudburst") {
|
||||||
|
const serverProcess = this.serverProcess = child_process.spawn("java", [
|
||||||
|
"-XX:+UseG1GC",
|
||||||
|
"-XX:+ParallelRefProcEnabled",
|
||||||
|
"-XX:MaxGCPauseMillis=200",
|
||||||
|
"-XX:+UnlockExperimentalVMOptions",
|
||||||
|
"-XX:+DisableExplicitGC",
|
||||||
|
"-XX:+AlwaysPreTouch",
|
||||||
|
"-XX:G1NewSizePercent=30",
|
||||||
|
"-XX:G1MaxNewSizePercent=40",
|
||||||
|
"-XX:G1HeapRegionSize=8M",
|
||||||
|
"-XX:G1ReservePercent=20",
|
||||||
|
"-XX:G1HeapWastePercent=5",
|
||||||
|
"-XX:G1MixedGCCountTarget=4",
|
||||||
|
"-XX:InitiatingHeapOccupancyPercent=15",
|
||||||
|
"-XX:G1MixedGCLiveThresholdPercent=90",
|
||||||
|
"-XX:G1RSetUpdatingPauseTimePercent=5",
|
||||||
|
"-XX:SurvivorRatio=32",
|
||||||
|
"-XX:+PerfDisableSharedMem",
|
||||||
|
"-XX:MaxTenuringThreshold=1",
|
||||||
|
"-Dusing.aikars.flags=https://mcflags.emc.gs",
|
||||||
|
"-Daikars.new.flags=true",
|
||||||
|
"-jar", "server.jar",
|
||||||
|
], {
|
||||||
|
cwd: this.serverFolder,
|
||||||
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
});
|
||||||
|
|
||||||
|
serverProcess;
|
||||||
|
} else if (platform === "pocketmine") {
|
||||||
|
const phpBin = (await extendsFS.readdirV2(this.serverFolder)).find(file => file.endsWith("php")||file.endsWith("php.exe"));
|
||||||
|
if (!phpBin) throw new Error("Fist install php binary in server folder");
|
||||||
|
this.serverProcess = child_process.spawn(phpBin, ["server.phar", "--no-wizard"], {
|
||||||
|
cwd: this.serverFolder,
|
||||||
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
});
|
||||||
|
} else if (platform === "mojang") {
|
||||||
|
const fileExec = path.join(this.serverFolder, (await fs.readdir(this.serverFolder)).find(file => file.startsWith("bedrock_server")));
|
||||||
|
const serverProcess = this.serverProcess = child_process.spawn(fileExec, {
|
||||||
|
cwd: this.serverFolder,
|
||||||
|
stdio: ["pipe", "pipe", "pipe"]
|
||||||
|
});
|
||||||
|
serverProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
([
|
||||||
|
readline.createInterface(this.serverProcess.stdout),
|
||||||
|
readline.createInterface(this.serverProcess.stderr)
|
||||||
|
]).map(inter => inter.on("error", err => this.emit("error", err)).on("line", data => this.emit("logLine", typeof data === "string" ? data : data[0])));
|
||||||
|
|
||||||
|
return this.serverProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeLn(data: string|Buffer) {
|
||||||
|
this.serverProcess.stdin.write(data);
|
||||||
|
if (typeof data === "string" && !(data.trim().endsWith("\n"))) this.serverProcess.stdin.write("\n");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopServer() {
|
||||||
|
this.writeLn("stop");
|
||||||
|
return new Promise<{code: number, signal: NodeJS.Signals}>((done, reject) => this.serverProcess.once("error", reject).once("exit", (code, signal) => done({code, signal})));
|
||||||
|
}
|
||||||
|
|
||||||
|
async setPlayerPermission(playername: string, permission: P extends "mojang" ? "operator"|"member"|"visitor" : "admin"|"user") {
|
||||||
|
if (this.platform === "mojang") {
|
||||||
|
const permissions: {permission: "operator"|"member"|"visitor", xuid: string}[] = JSON.parse(await fs.readFile(path.join(this.serverFolder, "permissions.json"), "utf8"));
|
||||||
|
permissions.push({
|
||||||
|
permission: permission as any,
|
||||||
|
xuid: playername
|
||||||
|
});
|
||||||
|
await fs.writeFile(path.join(this.serverFolder, "permissions.json"), JSON.stringify(permissions));
|
||||||
|
if (this.serverProcess) this.writeLn("permission reload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async allowList(playername: string, options?: {xuid?: string, ignoresPlayerLimit?: boolean}) {
|
||||||
|
if (this.platform === "mojang") {
|
||||||
|
const permissions: {ignoresPlayerLimit: boolean, name: string, xuid?: string}[] = JSON.parse(await fs.readFile(path.join(this.serverFolder, "allowlist.json"), "utf8"));
|
||||||
|
await fs.writeFile(path.join(this.serverFolder, "allowlist.json"), JSON.stringify(permissions));
|
||||||
|
permissions.push({
|
||||||
|
name: playername,
|
||||||
|
ignoresPlayerLimit: options?.ignoresPlayerLimit ?? false,
|
||||||
|
xuid: options?.xuid
|
||||||
|
});
|
||||||
|
if (this.serverProcess) this.writeLn("allowlist reload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
// Bedrock platform
|
// Bedrock platform
|
||||||
export * as Bedrock from "./platform/bedrock/index.js";
|
export * as Bedrock from "./platform/bedrock/index.js";
|
||||||
export * as bedrock from "./platform/bedrock/index.js";
|
export * as bedrock from "./platform/bedrock/index.js";
|
||||||
|
export { isBedrock } from "./platform/bedrock/index.js";
|
||||||
|
|
||||||
// Java platform
|
// Java platform
|
||||||
export * as Java from "./platform/java/index.js";
|
export * as Java from "./platform/java/index.js";
|
||||||
|
@@ -1,46 +1,15 @@
|
|||||||
import { extendsFS } from "@sirherobrine23/extends";
|
|
||||||
import child_process from "node:child_process";
|
|
||||||
import EventEmitter from "node:events";
|
import EventEmitter from "node:events";
|
||||||
import { createWriteStream, promises as fs } from "node:fs";
|
|
||||||
import { createInterface as readline } from "node:readline";
|
|
||||||
import path from "node:path";
|
|
||||||
import stream from "node:stream";
|
|
||||||
import tar from "tar";
|
|
||||||
|
|
||||||
export interface ManegerOptions {
|
export type EventMap = Record<string, (...args: any[]) => void>;
|
||||||
rootPath: string;
|
|
||||||
runServer: {
|
|
||||||
command: string;
|
|
||||||
args?: (string | number | boolean)[];
|
|
||||||
env?: Map<string, string | number | boolean> | { [K: string]: string | number | boolean },
|
|
||||||
stdio?: child_process.StdioPipeNamed | child_process.StdioPipe[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type EventMap = Record<string, (...args: any[]) => void>;
|
|
||||||
type EventKey<T extends EventMap> = string & keyof T;
|
type EventKey<T extends EventMap> = string & keyof T;
|
||||||
export type defaultEvents = {
|
|
||||||
"onServerSpawn": (child: child_process.ChildProcess) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
export type defineEvents<T extends EventMap> = T;
|
||||||
* Extens
|
|
||||||
*/
|
export class customEvent<T extends EventMap> extends EventEmitter {
|
||||||
export class serverManeger<T extends EventMap = {}> extends EventEmitter {
|
constructor() {
|
||||||
#io: ManegerOptions;
|
super({captureRejections: true});
|
||||||
#serverStorage: string;
|
|
||||||
#logStorage: string;
|
|
||||||
constructor(options: ManegerOptions) {
|
|
||||||
super({ captureRejections: true });
|
|
||||||
this.#io = options;
|
|
||||||
this.#io.rootPath = path.resolve(process.cwd(), this.#io.rootPath);
|
|
||||||
this.#serverStorage = path.join(this.#io.rootPath, "storage");
|
|
||||||
this.#logStorage = path.join(this.#io.rootPath, "logs");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
on<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
|
on<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
|
||||||
on<K extends EventKey<defaultEvents>>(eventName: K, fn: defaultEvents[K]): this;
|
|
||||||
on(eventName: "line", fn: (line: string) => void): this;
|
|
||||||
on(eventName: "error", fn: (err: Error) => void): this;
|
on(eventName: "error", fn: (err: Error) => void): this;
|
||||||
on(eventName: string, fn: (...args: any) => void): this {
|
on(eventName: string, fn: (...args: any) => void): this {
|
||||||
super.on(eventName, fn);
|
super.on(eventName, fn);
|
||||||
@@ -48,95 +17,42 @@ export class serverManeger<T extends EventMap = {}> extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
once<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
|
once<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
|
||||||
once<K extends EventKey<defaultEvents>>(eventName: K, fn: defaultEvents[K]): this;
|
|
||||||
once(eventName: "line", fn: (line: string) => void): this;
|
|
||||||
once(eventName: "error", fn: (err: Error) => void): this;
|
once(eventName: "error", fn: (err: Error) => void): this;
|
||||||
once(eventName: string, fn: (...args: any) => void): this {
|
once(eventName: string, fn: (...args: any) => void): this {
|
||||||
super.once(eventName, fn);
|
super.once(eventName, fn);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeListener<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
|
||||||
|
removeListener(eventName: "error", fn: (err: Error) => void): this;
|
||||||
|
removeListener(eventName: string, listener: (...args: any[]) => void): this {
|
||||||
|
super.removeListener(eventName, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
off<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
|
||||||
|
off(eventName: "error", fn: (err: Error) => void): this;
|
||||||
|
off(eventName: string, listener: (...args: any[]) => void): this {
|
||||||
|
super.off(eventName, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAllListeners<K extends EventKey<T>>(eventName: K): this;
|
||||||
|
removeAllListeners(event?: string): this {
|
||||||
|
super.removeAllListeners(event);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
emit<K extends EventKey<T>>(eventName: K, ...args: Parameters<T[K]>): boolean;
|
emit<K extends EventKey<T>>(eventName: K, ...args: Parameters<T[K]>): boolean;
|
||||||
emit<K extends EventKey<defaultEvents>>(eventName: K, ...args: Parameters<defaultEvents[K]>): boolean;
|
|
||||||
emit(name: "line", line: string): boolean;
|
|
||||||
emit(name: "error", err: Error): boolean;
|
emit(name: "error", err: Error): boolean;
|
||||||
emit(eventName: string, ...args: any): boolean {
|
emit(eventName: string, ...args: any): boolean {
|
||||||
return super.emit(eventName, args);
|
return super.emit(eventName, args);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** Get root from paths */
|
export class versionsStorages<T> extends Map<string, T> {
|
||||||
getRoot(): string { return this.#io.rootPath; };
|
get(key: string|number): T {
|
||||||
|
if (typeof key === "number") return super.get(Array.from(this.keys()).at(key));
|
||||||
/**
|
return super.get(key);
|
||||||
* Create tar.gz from server Storage, if server running create "snapshot" from server running state.
|
|
||||||
*
|
|
||||||
* @returns - Gzip tar
|
|
||||||
*/
|
|
||||||
hotBackup(): stream.Readable {
|
|
||||||
return tar.create({
|
|
||||||
gzip: true,
|
|
||||||
cwd: this.#serverStorage
|
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#severProcess: child_process.ChildProcess;
|
|
||||||
getStdout() {
|
|
||||||
return this.#severProcess.stdout;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStderr() {
|
|
||||||
return this.#severProcess.stderr;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStdin() {
|
|
||||||
return this.#severProcess.stdin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start server
|
|
||||||
*
|
|
||||||
* @returns get from server actions
|
|
||||||
*/
|
|
||||||
async startServer() {
|
|
||||||
if (this.#severProcess) return;
|
|
||||||
let processEnv: { [k: string]: string } = {};
|
|
||||||
if (this.#io.runServer.env) {
|
|
||||||
const { env } = this.#io.runServer;
|
|
||||||
if (env instanceof Map) {
|
|
||||||
processEnv = Array.from(env.keys()).reduce<typeof processEnv>((acc, keyName) => {
|
|
||||||
if (env.get(keyName) !== undefined) acc[keyName] = String(env.get(keyName));
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
} else {
|
|
||||||
processEnv = Object.keys(env).reduce<typeof processEnv>((acc, keyName) => {
|
|
||||||
if (env[keyName] !== undefined) acc[keyName] = String(env[keyName]);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const runDate = new Date();
|
|
||||||
this.#severProcess = child_process.spawn(this.#io.runServer.command, (this.#io.runServer.args || []).map(String), {
|
|
||||||
env: { ...process.env, ...processEnv },
|
|
||||||
cwd: this.#serverStorage,
|
|
||||||
stdio: this.#io.runServer.stdio,
|
|
||||||
});
|
|
||||||
|
|
||||||
const logPath = path.join(this.#logStorage, String(runDate.getFullYear()), String(runDate.getMonth() + 1), String(runDate.getDate()));
|
|
||||||
const logpathRoot = createWriteStream(path.join(this.#logStorage, "all.log"));
|
|
||||||
if (!(await extendsFS.exists(logPath))) await fs.mkdir(logPath, {recursive: true});
|
|
||||||
if (this.#severProcess.stdout) {
|
|
||||||
this.#severProcess.stdout.pipe(createWriteStream(path.join(logPath, "stdout.log")));
|
|
||||||
this.#severProcess.stdout.pipe(logpathRoot);
|
|
||||||
readline(this.#severProcess.stdout).on("line", line => this.emit("line", line));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#severProcess.stderr) {
|
|
||||||
this.#severProcess.stderr.pipe(createWriteStream(path.join(logPath, "stderr.log")));
|
|
||||||
this.#severProcess.stderr.pipe(logpathRoot);
|
|
||||||
readline(this.#severProcess.stderr).on("line", line => this.emit("line", line));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit("onServerSpawn", this.#severProcess);
|
|
||||||
}
|
|
||||||
};
|
|
12
packages/core/tests/bedrock.ts
Normal file
12
packages/core/tests/bedrock.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import { listVersion, Bedrock } from "../src/platform/bedrock/index.js";
|
||||||
|
import { homedir } from "node:os";
|
||||||
|
await listVersion.listMojang();
|
||||||
|
|
||||||
|
const mojang = new Bedrock(path.join(homedir(), ".bdsmaneger/playgroud/mojang"), "mojang");
|
||||||
|
const version = Array.from(listVersion.mojangCache.keys()).at(9);
|
||||||
|
console.log("Installing %s", version);
|
||||||
|
await mojang.installServer(version);
|
||||||
|
mojang.on("logLine", (line) => console.log(line[0]));
|
||||||
|
const pr = await mojang.runServer();
|
||||||
|
process.stdin.pipe(pr.stdin);
|
Reference in New Issue
Block a user