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 { versionsStorages } from "../../serverRun.js";
|
||||
import util from "node:util";
|
||||
import xml from "xml-js";
|
||||
|
||||
export interface mojangInfo {
|
||||
releaseDate: Date;
|
||||
release: "oficial" | "beta";
|
||||
files: { [P in NodeJS.Platform]?: { [A in NodeJS.Architecture]?: string } }
|
||||
date: Date,
|
||||
release?: "oficial" | "preview",
|
||||
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() {
|
||||
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");
|
||||
versions.forEach(rel => mojangCache.has(rel.version) ? null : mojangCache.set(rel.version, {
|
||||
releaseDate: new Date(rel.release),
|
||||
release: !rel.release ? "oficial" : rel.release === "preview" ? "beta" : "oficial",
|
||||
files: rel.url
|
||||
const versions = await http.jsonRequestBody<({version: string} & mojangInfo)[]>("https://raw.githubusercontent.com/Sirherobrine23/BedrockFetch/main/versions/all.json");
|
||||
versions.filter(ver => !(mojangCache.has(ver.version))).forEach(rel => mojangCache.set(rel.version, {
|
||||
date: new Date(rel.date),
|
||||
release: rel.release,
|
||||
url: rel.url
|
||||
}));
|
||||
}
|
||||
|
||||
@ -34,7 +41,7 @@ export interface powernukkitDownload {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const powernukkitCache = new Map<string, powernukkitDownload>();
|
||||
export const powernukkitCache = new versionsStorages<powernukkitDownload>();
|
||||
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 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;
|
||||
}
|
||||
|
||||
export const nukkitCache = new Map<string, cloudburstDownload>();
|
||||
export const cloudburstCache = new Map<string, cloudburstDownload>();
|
||||
export const nukkitCache = new versionsStorages<cloudburstDownload>();
|
||||
export const cloudburstCache = new versionsStorages<cloudburstDownload>();
|
||||
export async function listCloudburstProject() {
|
||||
const Projects = [ "Nukkit", "Server" ] as const;
|
||||
for (const Project of Projects) {
|
||||
@ -142,7 +149,7 @@ export interface pocketmineDownload {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const pocketmineCache = new Map<string, pocketmineDownload>();
|
||||
export const pocketmineCache = new versionsStorages<pocketmineDownload>();
|
||||
const pocketmineGithub = await Github.repositoryManeger("pmmp", "PocketMine-MP");
|
||||
export async function listPocketmineProject() {
|
||||
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 { serverManeger } from "../../serverRun.js";
|
||||
import semver from "semver";
|
||||
import { extendsFS } from "@sirherobrine23/extends";
|
||||
import { http } from "@sirherobrine23/http";
|
||||
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 async function installServer({platform, version}: {platform?: platforms, version?: string} = {}) {
|
||||
if (!platform) platform = "mojang";
|
||||
else if ((!(["mojang", "pocketmine", "powernukkit", "nukkit", "cloudburst"]).includes(platform))) throw new Error("Invalid platform");
|
||||
const versions = getCacheVersions();
|
||||
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);
|
||||
if (!version) version = "latest";
|
||||
if (platform === "mojang") {
|
||||
if (version === "latest") version = getLatest(Object.keys(versions.mojang));
|
||||
const release = versions.mojang[version];
|
||||
if (!release) throw new Error("Not valid Release");
|
||||
export type platforms = "mojang" | "pocketmine" | "powernukkit" | "nukkit" | "cloudburst";
|
||||
|
||||
export interface bedrockPorts { }
|
||||
|
||||
export interface playerInfo {
|
||||
connected: boolean;
|
||||
banned: boolean;
|
||||
historic: {
|
||||
action: "connected" | "spawned" | "disconnected";
|
||||
actionDate: Date;
|
||||
}[];
|
||||
};
|
||||
|
||||
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
|
||||
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
|
||||
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 { 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 {
|
||||
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>;
|
||||
export type EventMap = Record<string, (...args: any[]) => void>;
|
||||
type EventKey<T extends EventMap> = string & keyof T;
|
||||
export type defaultEvents = {
|
||||
"onServerSpawn": (child: child_process.ChildProcess) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extens
|
||||
*/
|
||||
export class serverManeger<T extends EventMap = {}> extends EventEmitter {
|
||||
#io: ManegerOptions;
|
||||
#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");
|
||||
export type defineEvents<T extends EventMap> = T;
|
||||
|
||||
export class customEvent<T extends EventMap> extends EventEmitter {
|
||||
constructor() {
|
||||
super({captureRejections: true});
|
||||
}
|
||||
|
||||
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: string, fn: (...args: any) => void): this {
|
||||
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<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: string, fn: (...args: any) => void): this {
|
||||
super.once(eventName, fn);
|
||||
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<defaultEvents>>(eventName: K, ...args: Parameters<defaultEvents[K]>): boolean;
|
||||
emit(name: "line", line: string): boolean;
|
||||
emit(name: "error", err: Error): boolean;
|
||||
emit(eventName: string, ...args: any): boolean {
|
||||
return super.emit(eventName, args);
|
||||
}
|
||||
};
|
||||
|
||||
/** Get root from paths */
|
||||
getRoot(): string { return this.#io.rootPath; };
|
||||
|
||||
/**
|
||||
* 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
|
||||
}, []);
|
||||
export class versionsStorages<T> extends Map<string, T> {
|
||||
get(key: string|number): T {
|
||||
if (typeof key === "number") return super.get(Array.from(this.keys()).at(key));
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
#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