Reewrite project #430

Merged
Sirherobrine23 merged 13 commits from reewriteProject into main 2022-08-31 10:03:14 +00:00
3 changed files with 136 additions and 3 deletions
Showing only changes of commit e7c3df9886 - Show all commits

View File

@ -3,8 +3,28 @@ import { execFile, exec as nodeExec, ExecFileOptions, ChildProcess } from "node:
import { EventEmitter } from "node:events";
import { promisify } from "node:util";
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 {
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 {
let childOptions: ObjectEncodingOptions & ExecFileOptions = {};
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 (childOptions?.env) childOptions.env = {...process.env, ...childOptions.env};
return new customChild(execFile(command, childArgs, childOptions));

21
src/httpRequest.ts Normal file
View 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
View 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));
}