Update http request and move versions to core #483

Merged
Sirherobrine23 merged 5 commits from internalServerVersion into main 2022-11-06 15:42:15 +00:00
27 changed files with 3279 additions and 335 deletions
Showing only changes of commit 8ff62c6008 - Show all commits

2132
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -36,18 +36,21 @@
"adm-zip": "^0.5.9",
"cron": "^2.1.0",
"got": "^12.5.2",
"jsdom": "^20.0.2",
"mongoose": "^6.7.1",
"prismarine-nbt": "^2.2.1",
"tar": "^6.1.11"
"tar": "^6.1.12"
},
"devDependencies": {
"@types/adm-zip": "^0.5.0",
"@types/cron": "^2.0.0",
"@types/jsdom": "^20.0.0",
"@types/mocha": "^10.0.0",
"@types/node": "^18.11.0",
"@types/node": "^18.11.9",
"@types/tar": "^6.1.3",
"mocha": "^10.1.0",
"ts-node": "^10.9.1",
"typedoc": "^0.23.16",
"typedoc": "^0.23.19",
"typescript": "^4.8.4"
}
}
}

View File

@ -3,7 +3,6 @@ import readline from "node:readline";
import child_process from "node:child_process";
import { EventEmitter } from "node:events";
import { bdsPlatform } from "./platformPathManeger";
import * as process_load from "./lib/processLoad";
export const internalSessions: {[sessionID: string]: serverActionV2} = {};
process.once("exit", () => Object.keys(internalSessions).forEach(id => internalSessions[id].stopServer()));
@ -45,10 +44,6 @@ export declare interface serverActionsEvent {
once(act: "log", fn: (data: string) => void): this;
emit(act: "log", data: string): boolean;
on(act: "avg", fn: (data: process_load.avg) => void): this;
once(act: "avg", fn: (data: process_load.avg) => void): this;
emit(act: "avg", data: process_load.avg): boolean;
on(act: "exit", fn: (data: {code: number, signal: NodeJS.Signals}) => void): this;
once(act: "exit", fn: (data: {code: number, signal: NodeJS.Signals}) => void): this;
emit(act: "exit", data: {code: number, signal: NodeJS.Signals}): boolean;
@ -134,10 +129,6 @@ export async function actionV2(options: {id: string, platform: bdsPlatform, proc
// Add to internal sessions
internalSessions[options.id] = serverObject;
serverObject.events.on("exit", () => delete internalSessions[options.id]);
const processMetric = new process_load.processLoad(childProcess.pid);
processMetric.on("avg", data => serverObject.events.emit("avg", data));
processMetric.on("error", err => serverObject.events.emit("error", err));
serverObject.events.on("exit", () => processMetric.close());
// Register hooks
// Server avaible to player

View File

@ -4,7 +4,6 @@ import * as platformPathManeger from "./platformPathManeger"
import * as globalPlatfroms from "./globalPlatfroms";
import * as pluginManeger from "./plugin/plugin";
import * as export_import from "./export_import";
import * as process_load from "./lib/processLoad";
import * as pluginHooks from "./plugin/hook";
import * as proxy from "./lib/proxy";
@ -16,7 +15,7 @@ import * as Spigot from "./spigot";
import * as Powernukkit from "./pwnuukit";
import * as PaperMC from "./paper";
export {platformPathManeger, globalPlatfroms, pluginManeger, export_import, process_load, PocketmineMP, pluginHooks, Powernukkit, httpRequest, PaperMC, Bedrock, Spigot, proxy, Java};
export {platformPathManeger, globalPlatfroms, pluginManeger, export_import, PocketmineMP, pluginHooks, Powernukkit, httpRequest, PaperMC, Bedrock, Spigot, proxy, Java};
export default {
Bedrock,
Java,
@ -31,7 +30,6 @@ export default {
pluginHooks,
httpRequest,
export_import,
process_load,
proxy
}
};
};

View File

@ -1,203 +0,0 @@
import { EventEmitter } from "node:events";
// import customProcess from "./childPromisses";
import extendsFs from "./extendsFs";
import path from "node:path";
import fs from "node:fs/promises";
// import os from "node:os";
export type pidstat = {
/** The process id. **/
pid: number,
/** The filename of the executable **/
exName: string,
/** 'R' is running, 'S' is sleeping, 'D' is sleeping in an uninterruptible wait, 'Z' is zombie, 'T' is traced or stopped **/
state: "R"|"S"|"D"|"Z"|"T",
/** effective user id **/
euid: number,
/** effective group id */
egid: number,
/** The pid of the parent. **/
ppid: number,
/** The pgrp of the process. **/
pgrp: number,
/** The session id of the process. **/
session: number,
/** The tty the process uses **/
tty: number,
/** (too long) **/
tpgid: number,
/** The flags of the process. **/
flags: number,
/** The number of minor faults **/
minflt: number,
/** The number of minor faults with childs **/
cminflt: number,
/** The number of major faults **/
majflt: number,
/** The number of major faults with childs **/
cmajflt: number,
/** user mode jiffies **/
utime: number,
/** kernel mode jiffies **/
stime: number,
/** user mode jiffies with childs **/
cutime: number,
/** kernel mode jiffies with childs **/
cstime: number,
/** process's next timeslice **/
counter: number,
/** the standard nice value, plus fifteen **/
priority: number,
/** The time in jiffies of the next timeout **/
timeout: number,
/** The time before the next SIGALRM is sent to the process **/
itrealvalue: number,
/** Time the process started after system boot **/
starttime: number,
/** Virtual memory size **/
vsize: number,
/** Resident Set Size **/
rss: number,
/** Current limit in bytes on the rss **/
rlim: number,
/** The address above which program text can run **/
startcode: number,
/** The address below which program text can run **/
endcode: number,
/** The address of the start of the stack **/
startstack: number,
/** The current value of ESP **/
kstkesp: number,
/** The current value of EIP **/
kstkeip: number,
/** The bitmap of pending signals **/
signal: number,
/** The bitmap of blocked signals **/
blocked: number,
/** The bitmap of ignored signals **/
sigignore: number,
/** The bitmap of catched signals **/
sigcatch: number,
/** (too long) **/
wchan: number,
/** scheduler **/
sched: number,
/** scheduler priority **/
sched_priority: any
};
// Receive: bytes packets errs drop fifo frame compressed multicast
// Transmit: bytes packets errs drop fifo colls carrier compressed
export type netDef = {
recive: {
bytes: number,
packets: number,
errs: number,
drop: number,
fifo: number,
frame: number,
compressed: number,
multicast: number
},
transmit: {
bytes: number,
packets: number,
errs: number,
drop: number,
fifo: number,
colls: number,
carrier: number,
compressed: number
}
};
export type avg = {cpuAvg: number, cpuAvgNoAbs: number, cwd: string, cmdline: string[], net: {[name: string]: netDef}}
export declare interface processLoad {
on(act: "error", fn: (data: any) => void): this;
once(act: "error", fn: (data: any) => void): this;
emit(act: "error", data: any): boolean;
on(act: "avg", fn: (data: avg & {latestLoop?: Date}) => void): this;
once(act: "avg", fn: (data: avg & {latestLoop?: Date}) => void): this;
emit(act: "avg", data: avg & {latestLoop?: Date}): boolean;
}
const linuxPlatform: NodeJS.Platform[] = ["android", "linux"];
export class processLoad extends EventEmitter {
pidNumber: number;
private terminateProcess = true;
#procFolder = "/proc";
close(){this.terminateProcess = false; return this;}
async #linux(): Promise<avg> {
const pidFolder = path.join(this.#procFolder, this.pidNumber.toFixed(0));
if (!await extendsFs.exists(pidFolder)) throw new Error("PID not found!");
// cwd
const cwd = await fs.realpath(path.join(pidFolder, "cwd"));
// cmdline
const cmdline = (await fs.readFile(path.join(pidFolder, "cmdline"), "utf8")).trim().split(/\u0000/).filter(a => !!a);
// stat
const [pid, exName, state, euid, egid, ppid, pgrp, session, tty, tpgid, flags, minflt, cminflt, majflt, cmajflt, utime, stime, cutime, cstime, counter, priority, timeout, itrealvalue, starttime, vsize, rss, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, sched, sched_priority] = (await fs.readFile(path.join(pidFolder, "stat"), "utf8")).trim().split(/\s+|[\t]+|\t/);
const cpuAvg: pidstat = {pid: parseInt(pid), exName, state: state as pidstat["state"], euid: parseInt(euid), egid: parseInt(egid), ppid: parseInt(ppid), pgrp: parseInt(pgrp), session: parseInt(session), tty: parseInt(tty), tpgid: parseInt(tpgid), flags: parseInt(flags), minflt: parseInt(minflt), cminflt: parseInt(cminflt), majflt: parseInt(majflt), cmajflt: parseInt(cmajflt), utime: parseInt(utime), stime: parseInt(stime), cutime: parseInt(cutime), cstime: parseInt(cstime), counter: parseInt(counter), priority: parseInt(priority), timeout: parseInt(timeout), itrealvalue: parseInt(itrealvalue), starttime: parseInt(starttime), vsize: parseInt(vsize), rss: parseInt(rss), rlim: parseInt(rlim), startcode: parseInt(startcode), endcode: parseInt(endcode), startstack: parseInt(startstack), kstkesp: parseInt(kstkesp), kstkeip: parseInt(kstkeip), signal: parseInt(signal), blocked: parseInt(blocked), sigignore: parseInt(sigignore), sigcatch: parseInt(sigcatch), wchan: parseInt(wchan), sched: parseInt(sched), sched_priority: sched_priority?parseInt(sched_priority):sched_priority};
// net
const netInterface = /([a-zA-Z0-9_\-]+):\s+(.*)/;
const net: {[name: string]: netDef} = {};
(await fs.readFile(path.join(pidFolder, "net/dev"), "utf8")).split(/\r?\n/).filter(line => netInterface.test(line)).forEach(line => {
const dataMath = line.match(netInterface);
if (!dataMath) return;
const [, interfaceName, data] = dataMath;
const [recive_bytes, recive_packets, recive_errs, recive_drop, recive_fifo, recive_frame, recive_compressed, recive_multicast, transmit_bytes, transmit_packets, transmit_errs, transmit_drop, transmit_fifo, transmit_colls, transmit_carrier, transmit_compressed] = data.split(/\s+/).map(numberString => parseInt(numberString));
net[interfaceName] = {
recive: {
bytes: recive_bytes,
compressed: recive_compressed,
drop: recive_drop,
errs: recive_errs,
fifo: recive_fifo,
frame: recive_frame,
multicast: recive_multicast,
packets: recive_packets
},
transmit: {
bytes: transmit_bytes,
carrier: transmit_carrier,
colls: transmit_colls,
compressed: transmit_compressed,
drop: transmit_drop,
errs: transmit_errs,
fifo: transmit_fifo,
packets: transmit_packets
}
};
return;
});
const cpu_usage = Math.abs((cpuAvg.cmajflt+cpuAvg.utime+cpuAvg.stime+cpuAvg.cutime+cpuAvg.cstime)/30)//Hartz;
return {
cpuAvg: parseFloat(cpu_usage.toFixed(2)),
cpuAvgNoAbs: cpu_usage,
cwd,
cmdline,
net,
};
}
latestLoop: Date;
latestAvg?: avg;
constructor(processPid: number) {
super({captureRejections: false});
this.pidNumber = processPid;
(async () => {
while(this.terminateProcess) {
if (linuxPlatform.includes(process.platform)) await this.#linux().then(data => {this.latestAvg = data; this.latestLoop = new Date(); this.emit("avg", {...data, latestLoop: this.latestLoop});}).catch(err => {this.emit("error", err); this.close()});
await new Promise(done => setTimeout(done, 1800));
}
})();
}
}

26
src/lib/request/client.ts Normal file
View File

@ -0,0 +1,26 @@
import { getJSON } from "@http/simples";
export type testIp<protocolType extends "ipv4"|"ipv6" = "ipv4"> = {
ip: string,
type: protocolType,
subtype: string,
via: string,
padding: string,
asn: string,
asnlist: string,
asn_name: string,
country: string,
protocol: "HTTP/2.0"|"HTTP/1.1"|"HTTP/1.0"
};
export async function getExternalIP(): Promise<{ipv4: string, ipv6?: string, rawRequest?: {ipv4: testIp<"ipv4">, ipv6?: testIp<"ipv6">}}> {
const [ipv6, ipv4] = await Promise.all([
await getJSON<testIp<"ipv6">>("https://ipv6.lookup.test-ipv6.com/ip/").catch(() => undefined),
await getJSON<testIp<"ipv4">>("https://ipv4.lookup.test-ipv6.com/ip/")
]);
return {
ipv4: ipv4.ip,
ipv6: ipv6?.ip,
rawRequest: {ipv4, ipv6}
};
}

107
src/lib/request/github.ts Normal file
View File

@ -0,0 +1,107 @@
import { getJSON } from "@http/simples";
export type githubRelease = {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
tarball_url: string;
zipball_url: string;
body: string;
author: {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
};
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: string;
published_at: string;
assets: Array<{
url: string;
id: number;
node_id: string;
name: string;
label: string;
content_type: string;
state: string;
size: number;
download_count: number;
created_at: string;
updated_at: string;
browser_download_url: string;
uploader: {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
};
}>;
};
export async function GithubRelease(username: string, repo: string, releaseTag: string): Promise<githubRelease>;
export async function GithubRelease(username: string, repo: string): Promise<githubRelease[]>;
export async function GithubRelease(username: string): Promise<githubRelease[]>;
export async function GithubRelease(username: string, repo?: string, releaseTag?: string): Promise<githubRelease|githubRelease[]> {
let fullRepo = username;
if (!username) throw new Error("Repository is required, example: GithubRelease(\"Username/repo\") or GithubRelease(\"Username\", \"repo\")");
if (repo) {
if (!/\//.test(fullRepo)) fullRepo += "/"+repo;
}
if (releaseTag) return getJSON<githubRelease>(`https://api.github.com/repos/${fullRepo}/releases/tags/${releaseTag}`);
return getJSON<githubRelease[]>(`https://api.github.com/repos/${fullRepo}/releases?per_page=100`);
}
export type githubTree = {
"sha": string,
"url": string,
"truncated": boolean,
"tree": {
"path": string,
"mode": string,
"type": "blob"|"tree",
"sha": string,
"size": number,
"url": string
}[],
};
export async function githubTree(username: string, repo: string, tree: string = "main") {
const validate = /^[a-zA-Z0-9_\-]+$/;
if (!validate.test(username)) throw new Error("Invalid username");
if (!validate.test(repo)) throw new Error("Invalid repository name");
return getJSON<githubTree>(`https://api.github.com/repos/${username}/${repo}/git/trees/${tree}?recursive=true`);
}

53
src/lib/request/large.ts Normal file
View File

@ -0,0 +1,53 @@
import { requestOptions, pipeFetch } from "@http/simples";
import os from "node:os";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import tar from "tar";
import AdmZip from "adm-zip";
import { exists } from "../extendsFs";
export async function saveFile(request: string|requestOptions & {filePath?: string}) {
if (typeof request === "string") request = {url: request};
const filePath = request.filePath||path.join(os.tmpdir(), `raw_bdscore_${Date.now()}_${(path.parse(request.url||request.socket?.path||crypto.randomBytes(16).toString("hex"))).name}`);
await pipeFetch({...request, waitFinish: true, stream: fs.createWriteStream(filePath, {autoClose: false})});
return filePath;
}
export async function tarExtract(request: requestOptions & {folderPath?: string}) {
const folderToExtract = request.folderPath||path.join(os.tmpdir(), `raw_bdscore_${Date.now()}_${(path.parse(request.url||request.socket?.path||crypto.randomBytes(16).toString("hex"))).name}`);
if (!await exists(folderToExtract)) await fs.promises.mkdir(folderToExtract, {recursive: true});
await pipeFetch({
...request,
waitFinish: true,
stream: tar.extract({
cwd: folderToExtract,
noChmod: false,
noMtime: false,
preserveOwner: true,
keep: true,
p: true
})
});
return folderToExtract
}
const githubAchive = /github.com\/[\S\w]+\/[\S\w]+\/archive\//;
export async function extractZip(request: requestOptions & {folderTarget: string}) {
const zip = new AdmZip(await saveFile(request));
const targetFolder = githubAchive.test(request.url)?await fs.promises.mkdtemp(path.join(os.tmpdir(), "githubRoot_"), "utf8"):request.folderTarget;
await new Promise<void>((done, reject) => {
zip.extractAllToAsync(targetFolder, true, true, (err) => {
if (!err) return done();
return reject(err);
})
});
if (githubAchive.test(request.url)) {
const files = await fs.promises.readdir(targetFolder);
if (files.length === 0) throw new Error("Invalid extract");
await fs.promises.cp(path.join(targetFolder, files[0]), request.folderTarget, {recursive: true, force: true, preserveTimestamps: true, verbatimSymlinks: true});
return await fs.promises.rm(targetFolder, {recursive: true, force: true});
}
return;
}

View File

@ -0,0 +1,70 @@
import type { Method } from "got";
import * as fs from "node:fs";
import * as stream from "node:stream";
const UserAgent = "Mozilla/5.0 (Linux; Android 12; moto g(7) plus) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36";
export let got;
async function gotCjs(): Promise<(typeof import("got"))["default"]> {
if (got) return got;
const dyImport = (await (eval('import("got")') as Promise<typeof import("got")>)).default.extend({enableUnixSockets: true, headers: {"User-Agent": UserAgent, "USER-AGENT": UserAgent, "user-agent": UserAgent}});
got = dyImport;
return dyImport;
}
gotCjs().then(res => got = res);
export type requestOptions = {
url?: string,
socket?: {
socketPath: string,
path?: string,
}
method?: Method,
headers?: {[headerName: string]: string[]|string},
body?: any,
};
export async function pipeFetch(options: requestOptions & {stream: fs.WriteStream|stream.Writable, waitFinish?: boolean}) {
if (!(options?.url||options?.socket)) throw new Error("Host blank")
const urlRequest = (typeof options.url === "string")?options.url:`http://unix:${options.socket.socketPath}:${options.socket.path||"/"}`;
const gotStream = (await gotCjs()).stream(urlRequest, {
isStream: true,
headers: options.headers||{},
method: options.method||"GET",
json: options.body,
});
await new Promise<void>((done, reject) => {
gotStream.pipe(options.stream);
options.stream.on("error", reject);
gotStream.on("error", reject);
gotStream.once("end", () => {
if (options.waitFinish) return options.stream.once("finish", done);
return done();
});
});
}
export async function bufferFetch(options: string|requestOptions) {
if (typeof options === "string") options = {url: options};
if (!(options.url||options.socket)) throw new Error("Host blank")
const urlRequest = (typeof options.url === "string")?options.url:`http://unix:${options.socket.socketPath}:${options.socket.path||"/"}`;
return (await gotCjs())(urlRequest, {
headers: options.headers||{},
method: options.method||"GET",
json: options.body,
responseType: "buffer",
}).then(res => ({headers: res.headers, data: Buffer.from(res.body), response: res}));
}
export async function getJSON<JSONReturn = any>(request: string|requestOptions): Promise<JSONReturn> {
if (typeof request === "string") request = {url: request};
const res = (await bufferFetch(request)).data.toString("utf8");
return JSON.parse(res) as JSONReturn;
}
const httpUrl = /^http[s]:\/\/[a-z0-9\.\-_]+(|\/([\s\W\S]+?))/;
export async function urls(options: requestOptions|string): Promise<string[]> {
const data = (await bufferFetch(options)).data.toString("utf8");
return data.split(/["'<>]/gi).filter(line => httpUrl.test(line))
}

View File

@ -0,0 +1,38 @@
import mongoose from "mongoose";
import connection from "./connect";
import { Router } from "express";
export const app = Router();
export type bedrockSchema = {
version: string,
date: Date,
latest: boolean,
url: {
win32: string,
linux: string
}
};
export const bedrock = connection.model<bedrockSchema>("bedrock", new mongoose.Schema<bedrockSchema>({
version: {
type: String,
required: true,
unique: true
},
date: Date,
latest: Boolean,
url: {
win32: String,
linux: String
}
}));
app.get("/", ({res}) => bedrock.find().lean().then(data => res.json(data)));
app.get("/latest", async ({res}) => res.json(await bedrock.findOne({latest: true}).lean()));
app.get("/search", async (req, res) => {
let version = req.query.version as string;
if (!version) return res.status(400).json({error: "No version specified"});
const versionFinded = await bedrock.findOne({version}).lean();
if (!versionFinded) return res.status(404).json({error: "Version not found"});
return res.json(versionFinded);
});

View File

@ -0,0 +1,8 @@
import mongoose from "mongoose";
export let authUsername = (!!process.env.MONGO_USER)?process.env.MONGO_USER:"public";
export let authPassword = (!!process.env.MONGO_PASSWORD)?process.env.MONGO_PASSWORD:"n0v8IBKEI920sfy8";
export const mongoURL = process.env.BDSVERSIONDB||`mongodb+srv://${authUsername}:${authPassword}@versionstorage.qgh8v.mongodb.net/v2_versions`;
// Connect to MongoDB
export const connection = mongoose.createConnection(mongoURL);
export default connection;

View File

@ -0,0 +1,33 @@
import mongoose from "mongoose";
import connection from "./connect";
import { Router } from "express";
export const app = Router();
export type javaSchema = {
version: string,
date: Date,
latest: boolean,
url: string
};
export const java = connection.model<javaSchema>("java", new mongoose.Schema<javaSchema>({
version: {
type: String,
required: true,
unique: true
},
date: Date,
latest: Boolean,
url: String
}));
export default java;
app.get("/", ({res}) => java.find().lean().then(data => res.json(data)));
app.get("/latest", async ({res}) => res.json(await java.findOne({latest: true}).lean()));
app.get("/search", async (req, res) => {
let version = req.query.version as string;
if (!version) return res.status(400).json({error: "No version specified"});
const versionFinded = await java.findOne({version: version}).lean();
if (!versionFinded) return res.status(404).json({error: "Version not found"});
return res.json(versionFinded);
});

View File

@ -0,0 +1,42 @@
import mongoose from "mongoose";
import connection from "./connect";
import { Router } from "express";
export const app = Router();
export type paperSchema = {
version: string,
build: number,
date: Date,
latest: boolean,
url: string
};
export const paper = connection.model<paperSchema>("paper", new mongoose.Schema<paperSchema>({
version: String,
build: Number,
date: Date,
latest: Boolean,
url: String
}));
app.get("/", ({res}) => paper.find().lean().then(data => res.json(data)));
app.get("/latest", ({res}) => paper.findOne({latest: true}).lean().then(data => res.json(data)));
app.get("/search", async (req, res) => {
let version = req.query.version as string;
if (!version) return res.status(400).json({erro: "Not allowd blank version"});
let build = parseInt(req.query.build as string);
if (isNaN(build)) build = undefined;
if (!!build) {
const info = await paper.findOne({
version,
build
});
if (!info) return res.status(400).json({error: "version and build not exists"});
return res.json(info);
}
const info = await paper.findOne({
version
});
if (!info) return res.status(400).json({error: "version not exists"});
return res.json(info);
});

View File

@ -0,0 +1,21 @@
import mongoose from "mongoose";
import connection from "./connect";
export type pocketminemmpSchema = {
version: string,
date: Date,
latest: boolean,
url: string
};
export const pocketmine = connection.model<pocketminemmpSchema>("pocketminemmp", new mongoose.Schema<pocketminemmpSchema>({
version: {
type: String,
required: true,
unique: true
},
date: Date,
latest: Boolean,
url: String
}));

View File

@ -0,0 +1,37 @@
import mongoose from "mongoose";
import connection from "./connect";
import { Router } from "express";
export const app = Router();
export type powernukkitSchema = {
version: string,
mcpeVersion: string,
date: Date,
latest: boolean,
url: string,
variantType: "stable"|"snapshot"
};
export const powernukkit = connection.model<powernukkitSchema>("powernukkit", new mongoose.Schema<powernukkitSchema>({
version: {
type: String,
unique: false,
required: true
},
mcpeVersion: String,
date: Date,
url: String,
variantType: String,
latest: Boolean
}));
app.get("/", ({res}) => powernukkit.find().lean().then(data => res.json(data)));
app.get("/latest", ({res}) => powernukkit.findOne({latest: true}).lean().then(data => res.json(data)));
app.get("/search", async (req, res) => {
let version = req.query.version as string;
let variant = (req.query.variant as string)||undefined;
if (!version) return res.status(400).json({error: "No version specified"});
const versionDB = await powernukkit.findOne({version, variant: {to: variant}}).lean();
if (!versionDB) return res.status(404).json({error: "Version not found"});
return res.json(versionDB);
});

View File

@ -0,0 +1,33 @@
import mongoose from "mongoose";
import connection from "./connect";
import { Router } from "express";
export const app = Router();
// Type to represent the spigot model
export type spigotSchema = {
version: string,
date: Date,
latest: boolean,
url: string
};
export const spigot = connection.model<spigotSchema>("spigot", new mongoose.Schema<spigotSchema>({
version: {
type: String,
required: true,
unique: true
},
date: Date,
latest: Boolean,
url: String
}));
app.get("/", ({res}) => spigot.find().lean().then(data => res.json(data)));
app.get("/latest", async ({res}) => res.json(await spigot.findOne({latest: true}).lean()));
app.get("/search", async (req, res) => {
let version = req.query.version as string;
if (!version) return res.status(400).json({error: "No version specified"});
const versionFinded = await spigot.findOne({version}).lean();
if (!versionFinded) return res.status(404).json({error: "Version not found"});
return res.json(versionFinded);
});

View File

@ -0,0 +1,76 @@
import fsOld from "node:fs";
import path from "node:path";
import fs from "node:fs/promises";
import { findVersion as getVersions, bedrock, java, paper, pocketmine, powernukkit, spigot } from "./index";
const rootVersions = path.join(process.cwd(), "versions");
const Bedrock = path.join(rootVersions, "bedrock");
const Pocketmine = path.join(rootVersions, "pocketmine");
const Powernukkit = path.join(rootVersions, "powernukkit");
const Java = path.join(rootVersions, "java");
const Spigot = path.join(rootVersions, "spigot");
const Paper = path.join(rootVersions, "paper");
(async function(){
if (!fsOld.existsSync(rootVersions)) await fs.mkdir(rootVersions, {recursive: true});
if (!fsOld.existsSync(Bedrock)) await fs.mkdir(Bedrock, {recursive: true});
if (!fsOld.existsSync(Pocketmine)) await fs.mkdir(Pocketmine, {recursive: true});
if (!fsOld.existsSync(Powernukkit)) await fs.mkdir(Powernukkit, {recursive: true});
if (!fsOld.existsSync(Java)) await fs.mkdir(Java, {recursive: true});
if (!fsOld.existsSync(Spigot)) await fs.mkdir(Spigot, {recursive: true});
if (!fsOld.existsSync(Paper)) await fs.mkdir(Paper, {recursive: true});
const bedrockData = await getVersions<bedrock[]>("bedrock", "all", true);
fs.writeFile(path.join(Bedrock, "latest.json"), JSON.stringify(bedrockData.find(release => release.latest), null, 2));
fs.writeFile(path.join(Bedrock, "all.json"), JSON.stringify(bedrockData, null, 2));
await Promise.all(bedrockData.map(async releases => {
const version = path.join(Bedrock, `${releases.version}.json`);
await fs.writeFile(version, JSON.stringify(releases, null, 2));
await fs.utimes(version, new Date(releases.date), new Date(releases.date));
}));
const PocketmineData = await getVersions<pocketmine[]>("pocketmine", "all", true);
fs.writeFile(path.join(Pocketmine, "latest.json"), JSON.stringify(PocketmineData.find(release => release.latest), null, 2));
fs.writeFile(path.join(Pocketmine, "all.json"), JSON.stringify(PocketmineData, null, 2));
await Promise.all(PocketmineData.map(async releases => {
const version = path.join(Pocketmine, `${releases.version}.json`);
await fs.writeFile(version, JSON.stringify(releases, null, 2));
await fs.utimes(version, new Date(releases.date), new Date(releases.date));
}));
const PowernukkitData = await getVersions<powernukkit[]>("powernukkit", "all", true);
fs.writeFile(path.join(Powernukkit, "latest.json"), JSON.stringify(PowernukkitData.find(release => release.latest), null, 2));
fs.writeFile(path.join(Powernukkit, "all.json"), JSON.stringify(PowernukkitData, null, 2));
await Promise.all(PowernukkitData.map(async releases => {
const version = path.join(Powernukkit, `${releases.version}.json`);
await fs.writeFile(version, JSON.stringify(releases, null, 2));
await fs.utimes(version, new Date(releases.date), new Date(releases.date));
}));
const JavaData = await getVersions<java[]>("java", "all", true);
fs.writeFile(path.join(Java, "latest.json"), JSON.stringify(JavaData.find(release => release.latest), null, 2));
fs.writeFile(path.join(Java, "all.json"), JSON.stringify(JavaData, null, 2));
await Promise.all(JavaData.map(async releases => {
const version = path.join(Java, `${releases.version}.json`);
await fs.writeFile(version, JSON.stringify(releases, null, 2));
await fs.utimes(version, new Date(releases.date), new Date(releases.date));
}));
const SpigotData = await getVersions<spigot[]>("spigot", "all", true);
fs.writeFile(path.join(Spigot, "latest.json"), JSON.stringify(SpigotData.find(release => release.latest), null, 2));
fs.writeFile(path.join(Spigot, "all.json"), JSON.stringify(SpigotData, null, 2));
await Promise.all(SpigotData.map(async releases => {
const version = path.join(Spigot, `${releases.version}.json`);
await fs.writeFile(version, JSON.stringify(releases, null, 2));
await fs.utimes(version, new Date(releases.date), new Date(releases.date));
}));
const PaperData = await getVersions<paper[]>("paper", "all", true);
fs.writeFile(path.join(Paper, "latest.json"), JSON.stringify(PaperData.find(release => release.latest), null, 2));
fs.writeFile(path.join(Paper, "all.json"), JSON.stringify(PaperData, null, 2));
await Promise.all(PaperData.map(async releases => {
const version = path.join(Paper, `${releases.version}_${releases.build}.json`);
await fs.writeFile(version, JSON.stringify(releases, null, 2));
await fs.utimes(version, new Date(releases.date), new Date(releases.date));
}));
})();

View File

@ -0,0 +1,42 @@
import adm_zip from "adm-zip";
import { bufferFetch, urls } from "@http/simples";
import { bedrock, bedrockSchema } from "../db/bedrock";
export default async function UpdateDatabase() {
const minecraftUrls = (await urls("https://minecraft.net/en-us/download/server/bedrock")).filter(Link => /bin-.*\.zip/.test(Link));
const objURLs: {linux: string, win32: string} = {linux: undefined, win32: undefined};
minecraftUrls.forEach((url: string) => {
if (/darwin|macos|mac/.test(url)) console.log("Macos Are now supported: %s", url);
else if (/win/.test(url)) objURLs.win32 = url;
else if (/linux/.test(url)) objURLs.linux = url;
});
const anyZip = objURLs.win32||objURLs.linux;
if (!anyZip) throw new Error("cannot get url");
const [, mcpeVersion] = anyZip.match(/\/[a-zA-Z-_]+([0-9\.]+).zip$/)||[];
const mcpeDate = await new Promise<Date>(async resolve => {
const zip = new adm_zip((await bufferFetch(objURLs.linux)).data);
for (const entry of zip.getEntries()) {
if (entry.entryName === "bedrock_server") return resolve(entry.header.time);
};
return resolve(new Date());
});
if (!mcpeVersion) return;
const version: bedrockSchema = {
version: mcpeVersion,
date: mcpeDate,
latest: false,
url: {
linux: objURLs.linux,
win32: objURLs.win32
}
};
if (await bedrock.findOne({version: version.version}).lean()) {
console.log("Bedrock version %s are exists", version.version);
return;
}
await bedrock.create(version);
console.log("Bedrock adding new version %s", version.version);
await bedrock.findOneAndUpdate({latest: true}, {$set: {latest: false}});
const latest = (await bedrock.find().lean()).sort((a, b) => b.date.getTime() - a.date.getTime())[0];
await bedrock.findByIdAndUpdate(latest._id, {$set: {latest: true}});
}

View File

@ -0,0 +1,35 @@
import { getJSON } from "@http/simples";
import { java } from "../db/java";
import { javaRelease } from "./types/Java";
async function Add(Version: string, versionDate: Date, url: string): Promise<void> {
if (await java.findOne({ version: Version }).lean().then(data => !!data).catch(() => true)) return;
else {
console.log("Java: Version '%s', url '%s'", Version, url);
await java.create({
version: Version,
date: versionDate,
latest: false,
url: url
});
}
}
type version_manifest_v2 = {latest: {release: string, snapshot: string, }, versions: {id: string, type: "snapshot"|"release", url: string, time: string, releaseTime: string, sha1: string, complianceLevel: number}[]}
async function Find() {
const Versions = await getJSON<version_manifest_v2>("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json");
for (const ver of Versions.versions.filter(a => a.type === "release")) {
const Release = await getJSON<javaRelease>(ver.url);
if (!!Release?.downloads?.server?.url) await Add(ver.id, new Date(ver.releaseTime), Release?.downloads?.server?.url);
}
return await java.findOneAndUpdate({version: Versions.latest.release}, {$set: {latest: true}}).lean();
}
export default async function UpdateDatabase() {
const latestVersion = await java.findOneAndUpdate({latest: true}, {$set: {latest: false}}).lean();
return {
new: await Find(),
old: latestVersion
};
}

View File

@ -0,0 +1,48 @@
import { getJSON as getJson } from "@http/simples";
import { paper } from "../db/paper";
type paperVersions = {
project_id: string,
project_name: string,
version_groups: string[],
versions: string[]
};
type paperBuilds = {
project_id: string,
project_name: string,
version: string,
builds: {
build: number;
time: string;
channel: string;
promoted: boolean;
changes: {commit: string, summary: string, message: string}[];
downloads: {
application: {name: string, sha256: string},
mojangMappings: {name: string, sha256: string}
}
}[]
};
export default async function find() {
const versions = (await getJson<paperVersions>("https://api.papermc.io/v2/projects/paper")).versions;
for (const version of versions) {
const builds = await getJson<paperBuilds>(`https://api.papermc.io/v2/projects/paper/versions/${version}/builds`);
await Promise.all(builds.builds.map(async function(build){
const downloadUrl = `https://api.papermc.io/v2/projects/paper/versions/${builds.version}/builds/${build.build}/downloads/${build.downloads.application.name}`;
if (await paper.findOne({url: downloadUrl}).lean()) return;
await paper.create({
version: builds.version,
build: build.build,
date: new Date(build.time),
url: downloadUrl,
latest: false
});
return console.log("Paper add %s version, build %s", builds.version, build.build);
}));
}
await paper.findOneAndUpdate({latest: true}, {$set: {latest: false}}).lean();
const latestVersionByDate = (await paper.find().lean()).sort((a, b) => b.date.getTime()-a.date.getTime())[0];
await paper.findByIdAndUpdate(latestVersionByDate._id, {$set: {latest: true}}).lean();
}

View File

@ -0,0 +1,40 @@
import { GithubRelease } from "@http/github";
import { pocketmine as pocketminemmp} from "../db/pocketmine";
async function Add(Version: string, versionDate: Date, url: string) {
if (await pocketminemmp.findOne({ version: Version }).lean().then(data => !!data).catch(() => true)) console.log("Pocketmine: version (%s) already exists", Version);
else {
await pocketminemmp.create({
version: Version,
date: versionDate,
latest: false,
url: url
});
console.log("Pocketmine PMMP: Version %s, url %s", Version, url);
}
}
async function Find() {
return await Promise.all((await GithubRelease("pmmp/PocketMine-MP")).filter(Release => !/beta|alpha/gi.test(Release.tag_name.toLowerCase())).map(Release => {
Release.assets = Release.assets.filter(asset => asset.name.endsWith(".phar"));
return Release;
}).filter(a => a.assets.length > 0).map(release => {
return Add(release.tag_name, new Date(release.published_at), release.assets[0].browser_download_url).catch(err => {
console.log("Pocketmine PMMP: Version %s, Error: %o", release.tag_name, err);
}).then(() => ({
Date: new Date(release.published_at),
Version: release.tag_name,
url: release.assets[0].browser_download_url
}));
}));
}
export default async function UpdateDatabase() {
const latestVersion = await pocketminemmp.findOneAndUpdate({ latest: true }, {$set: {latest: false}}).lean();
const Releases = await Find();
const newLatest = await pocketminemmp.findOneAndUpdate({ version: Releases[0].Version }, {$set: { latest: true }}).lean();
return {
new: newLatest,
old: latestVersion
};
}

View File

@ -0,0 +1,161 @@
import { powernukkit, powernukkitSchema } from "../db/powernukkit";
import { getJSON as getJson } from "@http/simples";
export const exportUrl = "https://raw.githubusercontent.com/PowerNukkit/powernukkit-version-aggregator/master/powernukkit-versions.json";
export type Release = {
version: string,
releaseTime: number,
minecraftVersion: string,
artefacts: string[],
commitId: string,
snapshotBuild?: number
}
export type PowernukkitVersions = {
releases: Release[],
snapshots: Release[]
}
function buildVersion(data: Release): powernukkitSchema|void {
let artefacts: {[key: string]: any} = {};
data.artefacts.forEach(function(artefactId) {
artefacts[artefactId] = buildArtefactUrl(data, artefactId);
});
if (data.commitId) {
artefacts["GIT_SOURCE"] = buildArtefactUrl(data, "GIT_SOURCE");
}
if (!data.snapshotBuild) {
artefacts["ONLINE_DOC"] = buildArtefactUrl(data, "ONLINE_DOC");
}
const releaseTime = new Date(data.releaseTime);
let url = getBestDownloadUrl(artefacts);
if (!url) return;
const schema: powernukkitSchema = {
version: data.version,
mcpeVersion: data.minecraftVersion,
date: releaseTime,
latest: false,
variantType: data.snapshotBuild === undefined ? "stable":"snapshot",
url
};
return schema;
}
function getBestDownloadUrl(artefacts): string {
if (artefacts.SHADED_JAR) {
return artefacts.SHADED_JAR;
} else {
return artefacts.REDUCED_JAR;
}
}
function buildArtefactUrl(data: any, artefactId?: string): string|void {
if (artefactId == "GIT_SOURCE") {
return buildGitSourceUrl(data);
} else if (artefactId == "ONLINE_DOC") {
return buildOnlineDocUrl(data);
} else if (data.snapshotBuild) {
return buildSnapshotArtefactUrl(data, artefactId);
} else {
return buildReleaseArtefactUrl(data, artefactId);
}
}
function buildOnlineDocUrl(data: any) {
if (data.snapshotBuild) {
if (data.artefacts.includes("JAVADOC_JAR")) {
return buildSnapshotArtefactUrl(data, "JAVADOC_JAR");
}
}
return "https://devs.powernukkit.org/#javadoc";
}
function buildGitSourceUrl(data) {
if (data.commitId) {
return "https://github.com/PowerNukkit/PowerNukkit/tree/" + data.commitId;
} else if (data.snapshotBuild) {
if (data.artefacts.includes("SHADED_SOURCES_JAR")) {
return buildSnapshotArtefactUrl(data, "SHADED_SOURCES_JAR");
} else if (data.artefacts.includes("REDUCED_SOURCES_JAR")) {
return buildSnapshotArtefactUrl(data, "REDUCED_SOURCES_JAR");
}
} else {
if (data.artefacts.includes("SHADED_SOURCES_JAR")) {
return buildReleaseArtefactUrl(data, "SHADED_SOURCES_JAR");
} else if (data.artefacts.includes("REDUCED_SOURCES_JAR")) {
return buildReleaseArtefactUrl(data, "REDUCED_SOURCES_JAR");
}
}
}
function buildReleaseArtefactUrl(data: any, artefactId?: string): string|void {
if (!data.artefacts.includes(artefactId)) {
return;
}
return "https://search.maven.org/remotecontent?filepath=org/powernukkit/powernukkit/" +
data.version +
"/powernukkit-" +
data.version +
getArtefactExtension(artefactId);
}
function buildSnapshotArtefactUrl(data: any, artefactId?: string): string|void {
if (!data.artefacts.includes(artefactId)) {
return;
}
let dt = new Date(data.releaseTime);
let snapshotCode = dt.getUTCFullYear().toString().padStart(4, "0") +
(dt.getUTCMonth() + 1).toString().padStart(2, "0") +
dt.getUTCDate().toString().padStart(2, "0") +
"." +
dt.getUTCHours().toString().padStart(2, "0") +
dt.getUTCMinutes().toString().padStart(2, "0") +
dt.getUTCSeconds().toString().padStart(2, "0") +
"-" +
data.snapshotBuild;
let snapshotIndex = data.version.indexOf("-SNAPSHOT");
let version = data.version.substring(0, snapshotIndex);
let extension = getArtefactExtension(artefactId);
return "https://oss.sonatype.org/content/repositories/snapshots/org/powernukkit/powernukkit" +
"/" +
version + "-SNAPSHOT" +
"/" +
"powernukkit-" + version +
"-" +
snapshotCode +
extension
}
function getArtefactExtension(artefactId) {
let extension = ".unknown";
switch (artefactId) {
case "REDUCED_JAR": extension = ".jar"; break;
case "REDUCED_SOURCES_JAR": extension = "-sources.jar"; break;
case "SHADED_JAR": extension = "-shaded.jar"; break;
case "SHADED_SOURCES_JAR": extension = "-shaded-sources.jar"; break;
case "JAVADOC_JAR": extension = "-javadoc.jar"; break;
}
return extension;
}
export default async function find() {
const releases_version = await getJson(exportUrl) as PowernukkitVersions;
for (const stable of releases_version.releases) {
const data = buildVersion(stable);
if (!data) continue
if (await powernukkit.findOne({version: data.version}).lean()) continue;
await powernukkit.create(data);
console.log("Powernukkit stable add %s version to minecraft bedrock %s version", data.version, data.mcpeVersion);
}
for (const snapshot of releases_version.snapshots) {
const data = buildVersion(snapshot);
if (!data) continue
if (await powernukkit.findOne({version: data.version}).lean()) continue;
await powernukkit.create(data);
console.log("Powernukkit snapshort add %s version to minecraft bedrock %s version", data.version, data.mcpeVersion);
}
const oldLatest = await powernukkit.findOneAndUpdate({latest: true}, {$set: {latest: false}}).lean();
const latestVersion = (await powernukkit.find({variantType: "stable"}).lean()).sort((b, a) => a.date.getTime()-b.date.getTime())[0];
await powernukkit.findOneAndUpdate({version: latestVersion.version, variant: {variantType: "stable"}}, {$set: {latest: true}}).lean().catch(err => powernukkit.findOneAndUpdate({version: oldLatest.version}, {$set: {latest: true}}).lean().then(() => Promise.reject(err)));
return;
}

View File

@ -0,0 +1,35 @@
import jsdom from "jsdom";
import { bufferFetch } from "@http/simples";
import {spigot} from "../db/spigot";
export const urlRegex = /http[s]:\/\/.*/;
async function Find() {
const { document } = (new jsdom.JSDOM(await bufferFetch("https://getbukkit.org/download/spigot").then(({data}) => data.toString("utf8")).catch(err => {console.log(err); return "<html></html>"}))).window;
const Versions = await Promise.all(([...document.querySelectorAll("#download > div > div > div > div")]).map(async DOM => {
const download = (new jsdom.JSDOM(await bufferFetch(DOM.querySelector("div > div.col-sm-4 > div.btn-group > a")["href"]).then(({data}) => data.toString("utf8"))));
const serverInfo = {
version: String(DOM.querySelector("div:nth-child(1) > h2").textContent),
Date: new Date(DOM.querySelector("div:nth-child(3) > h3").textContent),
url: download.window.document.querySelector("#get-download > div > div > div:nth-child(2) > div > h2 > a")["href"]
}
if (!urlRegex.test(serverInfo.url)||!serverInfo.url) return null;
return serverInfo;
}));
for (const Version of Versions.filter(a => a)) {
if (await spigot.findOne({version: Version.version}).lean()) continue;
console.log("Spigot", Version.version, Version.url);
await spigot.create({
version: Version.version,
date: Version.Date,
latest: false,
url: Version.url
});
}
await spigot.findOneAndUpdate({latest: true}, {$set: {latest: false}}).lean();
const latestVersion = (await spigot.find().lean()).sort((b, a) => a.date.getTime()-b.date.getTime())[0];
await spigot.findByIdAndUpdate(latestVersion._id, {$set: {latest: true}});
}
export default async function UpdateDatabase() {
await Find();
}

View File

@ -0,0 +1,53 @@
#!/usr/bin/env node
import yargs from "yargs";
import bedrock from "./Bedrock";
import pocketmine from "./Pocketmine";
import java from "./Java";
import spigot from "./Spigot";
import paper from "./Paper";
import powernukkit from "./Powernukkit"
const options = yargs(process.argv.slice(2)).help().version(false).alias("h", "help").wrap(yargs.terminalWidth()).options("all", {
description: "Fetch all plaftorms",
type: "boolean"
}).option("bedrock", {
description: "Fetch Bedrock versions",
type: "boolean"
}).option("java", {
description: "Fetch Java versions",
type: "boolean"
}).option("pocketmine", {
description: "Fetch Pocketmine-MP versions",
type: "boolean"
}).option("spigot", {
description: "Fetch Spigot versions",
type: "boolean"
}).option("paper", {
description: "Fetch Paper versions",
type: "boolean"
}).option("powernukkit", {
description: "Fetch Powernukkit versions",
type: "boolean"
})
.parseSync();
async function all() {
await bedrock()
await java();
await pocketmine();
await spigot();
await paper();
await powernukkit();
}
if (options.bedrock) bedrock().then(() => {console.log("Bedrock sucess update"); process.exit(0)}).catch(err => {console.log("Bedrock catch Error: %s", String(err)); process.exit(1)});
else if (options.java) java().then(() => {console.log("Java sucess update"); process.exit(0)}).catch(err => {console.log("Java catch Error: %s", String(err)); process.exit(1)});
else if (options.spigot) spigot().then(() => {console.log("Spigot sucess update"); process.exit(0)}).catch(err => {console.log("Spigot catch Error: %s", String(err)); process.exit(1)});
else if (options.pocketmine) pocketmine().then(() => {console.log("Pocketmine sucess update"); process.exit(0)}).catch(err => {console.log("Pocketmine catch Error: %s", String(err)); process.exit(1)});
else if (options.paper) paper().then(() => {console.log("Paper sucess update"); process.exit(0)}).catch(err => {console.log("Paper catch Error: %s", String(err)); process.exit(1)});
else if (options.powernukkit) powernukkit().then(() => {console.log("Powernukkit sucess update"); process.exit(0)}).catch(err => {console.log("Powernukkit catch Error: %s", String(err)); process.exit(1)});
else if (options.all) all().then(() => process.exit(0)).catch(err => {console.trace(err); process.exit(1)});
else {
console.log("No options set");
process.exit(1);
}

View File

@ -0,0 +1,165 @@
export type javaRelease = {
arguments: {
game: Array<any>
jvm: [
{
rules: Array<{
action: string
os: {
name: string
}
}>
value: Array<string>
},
{
rules: Array<{
action: string
os: {
name: string
}
}>
value: string
},
{
rules: Array<{
action: string
os: {
name: string
version: string
}
}>
value: Array<string>
},
{
rules: Array<{
action: string
os: {
arch: string
}
}>
value: string
},
string,
string,
string,
string,
string
]
}
assetIndex: {
id: string
sha1: string
size: number
totalSize: number
url: string
}
assets: string
complianceLevel: number
downloads: {
client: {
sha1: string
size: number
url: string
}
client_mappings: {
sha1: string
size: number
url: string
}
server: {
sha1: string
size: number
url: string
}
server_mappings: {
sha1: string
size: number
url: string
}
}
id: string
javaVersion: {
component: string
majorVersion: number
}
libraries: Array<{
downloads: {
artifact: {
path: string
sha1: string
size: number
url: string
}
classifiers?: {
javadoc?: {
path: string
sha1: string
size: number
url: string
}
"natives-osx"?: {
path: string
sha1: string
size: number
url: string
}
sources?: {
path: string
sha1: string
size: number
url: string
}
"natives-linux"?: {
path: string
sha1: string
size: number
url: string
}
"natives-windows"?: {
path: string
sha1: string
size: number
url: string
}
"natives-macos"?: {
path: string
sha1: string
size: number
url: string
}
}
}
name: string
rules?: Array<{
action: string
os?: {
name: string
}
}>
natives?: {
osx?: string
linux?: string
windows?: string
}
extract?: {
exclude: Array<string>
}
}>
logging: {
client: {
argument: string
file: {
id: string
sha1: string
size: number
url: string
}
type: string
}
}
mainClass: string
minimumLauncherVersion: number
releaseTime: string
time: string
type: string
}

76
src/lib/versions/index.ts Normal file
View File

@ -0,0 +1,76 @@
import { bufferFetch } from "@http/simples"
import type { bedrockSchema } from "./db/bedrock";
import type { javaSchema } from "./db/java";
import type { paperSchema } from "./db/paper";
import type { powernukkitSchema } from "./db/powernukkit";
import type { pocketminemmpSchema } from "./db/pocketmine";
import type { spigotSchema } from "./db/spigot";
export type BdsCorePlatforms = "bedrock"|"java"|"paper"|"powernukkit"|"pocketmine"|"spigot";
export type all = bedrockSchema|javaSchema|powernukkitSchema|paperSchema|pocketminemmpSchema|spigotSchema
export type {
bedrockSchema as bedrock,
javaSchema as java,
paperSchema as paper,
pocketminemmpSchema as pocketmine,
spigotSchema as spigot,
powernukkitSchema as powernukkit
}
export async function findVersion<PlatformSchema = all[]>(bdsPlaform: BdsCorePlatforms): Promise<PlatformSchema>;
export async function findVersion<PlatformSchema = all>(bdsPlaform: BdsCorePlatforms, version: string|boolean): Promise<PlatformSchema>;
export async function findVersion<PlatformSchema = all>(bdsPlaform: BdsCorePlatforms, version: string|boolean, ignoreStatic: boolean): Promise<PlatformSchema>;
export async function findVersion<PlatformSchema = all|all[]>(bdsPlaform: BdsCorePlatforms, version?: string|boolean, ignoreStatic?: boolean): Promise<PlatformSchema> {
const versionURLs = ["https://mcpeversions_backup.sirherobrine23.org", "https://mcpeversions.sirherobrine23.org"];
if (!ignoreStatic) versionURLs.push("https://mcpeversion-static.sirherobrine23.org/"); else console.warn("Using dynamic APIs, some may be down!");
for (let url of versionURLs.reverse()) {
url += "/"+bdsPlaform;
if (/static/.test(url)) {
if (version === undefined) url += "/all.json";
else if (typeof version === "boolean") url += "/latest.json";
else url += `/${version}.json`;
} else {
if (version === undefined||version === "all") url += "/";
else {
if (typeof version === "boolean"||version === "latest") url += "/latest";
else url += `/search?version=${version}`;
}
}
const res = await bufferFetch(url).then(({data}) => data).catch(() => false);
if (res === false) continue;
const data = JSON.parse(res.toString("utf8"), (key, value) => key === "date" ? new Date(value):value);
if (!data) throw new Error("Failed to get data");
return data;
}
throw new Error("Failed to exec API request!");
}
export const platformManeger = {
bedrock: {
async all(){return findVersion<bedrockSchema[]>("bedrock");},
async find(version: string|boolean){return findVersion<bedrockSchema>("bedrock", version);}
},
pocketmine: {
async all(){return findVersion<pocketminemmpSchema[]>("pocketmine");},
async find(version: string|boolean){return findVersion<pocketminemmpSchema>("pocketmine", version);}
},
powernukkit: {
async all(){return findVersion<powernukkitSchema[]>("powernukkit");},
async find(version: string|boolean){return findVersion<powernukkitSchema>("powernukkit", version);}
},
java: {
async all(){return findVersion<javaSchema[]>("java");},
async find(version: string|boolean){return findVersion<javaSchema>("java", version);}
},
spigot: {
async all(){return findVersion<spigotSchema[]>("spigot");},
async find(version: string|boolean){return findVersion<spigotSchema>("spigot", version);}
},
paper: {
async all(){return findVersion<paperSchema[]>("paper");},
async find(version: string|boolean, build?: number|string){
if (!build) build = (await findVersion<paperSchema[]>("paper")).find(ver => ver.version === version)?.build;
return findVersion<paperSchema>("paper", `${version}${!!build?"_"+build:""}`);
}
}
};

View File

@ -1,29 +1,29 @@
{
"compilerOptions": {
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "ES2019",
// "outDir": "./dist",
"declaration": true,
"strict": false,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"allowJs": true,
"paths": {
"@/*": [
"./node_modules/@*"
]
}
},
"include": [
"src/**/*"
, "load.js" ],
"exclude": [
"node_modules/",
"test/",
"load.js"
]
"compilerOptions": {
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "ES2019",
"declaration": true,
"strict": false,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"allowJs": true,
"lib": ["ES6"],
"baseUrl": ".",
"paths": {
"@http/*": [
"./src/lib/request/*"
]
}
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules/",
"tests/"
]
}