Reewrite project #430
@ -3,8 +3,28 @@ import { execFile, exec as nodeExec, ExecFileOptions, ChildProcess } from "node:
|
|||||||
import { EventEmitter } from "node:events";
|
import { EventEmitter } from "node:events";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
export {execFile};
|
export {execFile};
|
||||||
export const execFileAsync = promisify(execFile);
|
export const execAsync = promisify(nodeExec);
|
||||||
export const execAsync = promisify(nodeExec)
|
// export const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
|
export type execOptions = ObjectEncodingOptions & ExecFileOptions & {stdio?: "ignore"|"inherit"};
|
||||||
|
export function execFileAsync(command: string): Promise<{stdout: string, stderr: string}>;
|
||||||
|
export function execFileAsync(command: string, args: string[]): Promise<{stdout: string, stderr: string}>;
|
||||||
|
export function execFileAsync(command: string, options: execOptions): Promise<{stdout: string, stderr: string}>;
|
||||||
|
export function execFileAsync(command: string, args: string[], options: execOptions): Promise<{stdout: string, stderr: string}>;
|
||||||
|
export function execFileAsync(command: string, args?: execOptions|string[], options?: execOptions) {
|
||||||
|
let childOptions: execOptions = {};
|
||||||
|
let childArgs: string[] = [];
|
||||||
|
if (args instanceof Array) childArgs = args; else if (args instanceof Object) childOptions = args as execOptions;
|
||||||
|
if (options) childOptions = options;
|
||||||
|
if (childOptions?.env) childOptions.env = {...process.env, ...childOptions.env};
|
||||||
|
return new Promise<{stdout: string, stderr: string}>((resolve, rejectExec) => {
|
||||||
|
const child = execFile(command, childArgs, childOptions, (err, out, err2) => {if (err) return rejectExec(err);resolve({stdout: out, stderr: err2});});
|
||||||
|
if (options?.stdio === "inherit") {
|
||||||
|
child.stdout.on("data", data => process.stdout.write(data));
|
||||||
|
child.stderr.on("data", data => process.stderr.write(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export class customChild {
|
export class customChild {
|
||||||
private eventMiter = new EventEmitter({captureRejections: false});
|
private eventMiter = new EventEmitter({captureRejections: false});
|
||||||
@ -71,7 +91,7 @@ export function exec(command: string, args: string[], options: ObjectEncodingOpt
|
|||||||
export function exec(command: string, args?: ObjectEncodingOptions & ExecFileOptions|string[], options?: ObjectEncodingOptions & ExecFileOptions): customChild {
|
export function exec(command: string, args?: ObjectEncodingOptions & ExecFileOptions|string[], options?: ObjectEncodingOptions & ExecFileOptions): customChild {
|
||||||
let childOptions: ObjectEncodingOptions & ExecFileOptions = {};
|
let childOptions: ObjectEncodingOptions & ExecFileOptions = {};
|
||||||
let childArgs: string[] = [];
|
let childArgs: string[] = [];
|
||||||
if (args instanceof Object) childOptions = args as ObjectEncodingOptions & ExecFileOptions; else if (args instanceof Array) childArgs = args;
|
if (args instanceof Array) childArgs = args; else if (args instanceof Object) childOptions = args as execOptions;
|
||||||
if (!options) childOptions = options;
|
if (!options) childOptions = options;
|
||||||
if (childOptions?.env) childOptions.env = {...process.env, ...childOptions.env};
|
if (childOptions?.env) childOptions.env = {...process.env, ...childOptions.env};
|
||||||
return new customChild(execFile(command, childArgs, childOptions));
|
return new customChild(execFile(command, childArgs, childOptions));
|
||||||
|
21
src/httpRequest.ts
Normal file
21
src/httpRequest.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export async function getBuffer(url: string, config?: {body?: any, header?: {[key: string]: string}}): Promise<Buffer> {
|
||||||
|
const Headers = {};
|
||||||
|
let Body: any;
|
||||||
|
if (config) {
|
||||||
|
if (config.header) Object.keys(config.header).forEach(key => Headers[key] = config.header[key]);
|
||||||
|
if (config.body) Body = config.body;
|
||||||
|
}
|
||||||
|
if (typeof fetch === "undefined") return axios.get(url, {
|
||||||
|
responseEncoding: "arraybuffer",
|
||||||
|
responseType: "arraybuffer",
|
||||||
|
headers: Headers,
|
||||||
|
data: Body
|
||||||
|
}).then(({data}) => Buffer.from(data));
|
||||||
|
return fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
body: typeof Body === "object" ? JSON.stringify(Body, null, 2):Body,
|
||||||
|
headers: Headers
|
||||||
|
}).then(res => res.arrayBuffer()).then(res => Buffer.from(res));
|
||||||
|
}
|
92
src/pocketmine.ts
Normal file
92
src/pocketmine.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import * as path from "node:path";
|
||||||
|
import * as fs from "node:fs/promises";
|
||||||
|
import * as os from "node:os";
|
||||||
|
import * as tar from "tar";
|
||||||
|
import { existsSync as fsExistsSync } from "node:fs";
|
||||||
|
import { getPocketminePhar, versionUrl } from "@the-bds-maneger/server_versions";
|
||||||
|
import { execFileAsync } from "./childPromisses";
|
||||||
|
import { serverRoot } from "./pathControl";
|
||||||
|
import { getBuffer } from "./httpRequest";
|
||||||
|
import {} from "./globalPlatfroms";
|
||||||
|
import AdmZip from "adm-zip";
|
||||||
|
import { promisify } from 'node:util';
|
||||||
|
export const serverPath = path.join(serverRoot, "pocketmine");
|
||||||
|
export const serverPhar = path.join(serverPath, "pocketmine.phar");
|
||||||
|
|
||||||
|
async function Readdir(pathRead: string, filter?: Array<RegExp>) {
|
||||||
|
if (!filter) filter = [/.*/];
|
||||||
|
const fixedPath = path.resolve(pathRead);
|
||||||
|
const files: Array<{
|
||||||
|
path: string,
|
||||||
|
name: string
|
||||||
|
}> = [];
|
||||||
|
for (const file of await fs.readdir(fixedPath)) {
|
||||||
|
const FullFilePath = path.join(fixedPath, file);
|
||||||
|
const stats = await fs.stat(FullFilePath);
|
||||||
|
if (stats.isDirectory()) files.push(...(await Readdir(FullFilePath, filter)));
|
||||||
|
else if (stats.isSymbolicLink()) {
|
||||||
|
const realPath = await fs.realpath(FullFilePath);
|
||||||
|
const statsSys = await fs.stat(realPath);
|
||||||
|
if (statsSys.isDirectory()) files.push(...(await Readdir(realPath, filter)));
|
||||||
|
else if (filter.length === 0||filter.some(x => x.test(realPath))) files.push({
|
||||||
|
path: FullFilePath,
|
||||||
|
name: path.basename(FullFilePath)
|
||||||
|
});
|
||||||
|
} else if (filter.length === 0||filter.some(x => x.test(FullFilePath))) files.push({
|
||||||
|
path: FullFilePath,
|
||||||
|
name: path.basename(FullFilePath)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildPhp() {
|
||||||
|
if (process.platform === "win32") throw new Error("Script is to Linux and MacOS");
|
||||||
|
const tempFolder = path.join(os.tmpdir(), "bdsPhp_"+(Math.random()*19999901).toString(16).replace(".", "").replace(/[0-9]/g, (_, a) =>a=="1"?"a":a=="2"?"b":a=="3"?"S":"k"));
|
||||||
|
if (!fsExistsSync(tempFolder)) fs.mkdir(tempFolder, {recursive: true});
|
||||||
|
await fs.writeFile(path.join(tempFolder, "build.sh"), await getBuffer("https://raw.githubusercontent.com/pmmp/php-build-scripts/stable/compile.sh"));
|
||||||
|
await fs.chmod(path.join(tempFolder, "build.sh"), "777");
|
||||||
|
console.info("Building PHP!");
|
||||||
|
await execFileAsync(path.join(tempFolder, "build.sh"), ["-j"+os.cpus().length], {cwd: tempFolder, stdio: "inherit"});
|
||||||
|
await fs.cp(path.join(tempFolder, "bin", (await fs.readdir(path.join(tempFolder, "bin")))[0]), path.join(serverPath, "bin"), {force: true, recursive: true, preserveTimestamps: true, verbatimSymlinks: true});
|
||||||
|
console.log("PHP Build success!");
|
||||||
|
}
|
||||||
|
buildPhp();
|
||||||
|
|
||||||
|
async function installPhp(): Promise<void> {
|
||||||
|
const file = (await getBuffer(`${versionUrl}/pocketmine/bin?os=${process.platform}&arch=${process.arch}`).then(res => JSON.parse(res.toString("utf8")) as {url: string, name: string}[]))[0];
|
||||||
|
if (!file) return buildPhp();
|
||||||
|
if (fsExistsSync(path.resolve(serverPath, "bin"))) await fs.rm(path.resolve(serverPath, "bin"), {recursive: true});
|
||||||
|
// Tar.gz
|
||||||
|
if (/tar\.gz/.test(file.name)) {
|
||||||
|
await fs.writeFile(path.join(os.tmpdir(), file.name), await getBuffer(file.url));
|
||||||
|
await tar.extract({
|
||||||
|
file: path.join(os.tmpdir(), file.name),
|
||||||
|
C: path.join(serverPath, "bin"),
|
||||||
|
keep: true,
|
||||||
|
p: true,
|
||||||
|
noChmod: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const zip = new AdmZip(await getBuffer(file.url));
|
||||||
|
await promisify(zip.extractAllToAsync)(serverPath, false, true);
|
||||||
|
}
|
||||||
|
if (process.platform === "linux"||process.platform === "android"||process.platform === "darwin") {
|
||||||
|
const ztsFind = await Readdir(path.resolve(serverPath, "bin"), [/.*debug-zts.*/]);
|
||||||
|
if (ztsFind.length === 0) return;
|
||||||
|
const phpIniPath = (await Readdir(path.resolve(serverPath, "bin"), [/php\.ini$/]))[0].path;
|
||||||
|
let phpIni = await fs.readFile(phpIniPath, "utf8");
|
||||||
|
if (phpIni.includes("extension_dir")) await fs.writeFile(phpIniPath, phpIni.replace(/extension_dir=.*/g, ""));
|
||||||
|
phpIni = phpIni+`\nextension_dir=${ztsFind[0].path}`
|
||||||
|
await fs.writeFile(phpIniPath, phpIni);
|
||||||
|
}
|
||||||
|
// test it's works php
|
||||||
|
await fs.writeFile(path.join(os.tmpdir(), "test.php"), `<?php echo "Hello World";`);
|
||||||
|
await execFileAsync(path.join(serverPath, "bin", process.platform === "win32" ? "php/php.exe" : "php"), ["-f", path.join(os.tmpdir(), "test.php")]).catch(buildPhp);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function installServer(version: string|boolean) {
|
||||||
|
if (!fsExistsSync(serverPath)) await fs.mkdir(serverPath, {recursive: true});
|
||||||
|
await installPhp();
|
||||||
|
await fs.writeFile(serverPhar, await getPocketminePhar(version));
|
||||||
|
}
|
Reference in New Issue
Block a user