Core apis #528

Merged
Sirherobrine23 merged 3 commits from coreApis into main 2023-06-12 01:39:52 +00:00
15 changed files with 543 additions and 302 deletions
Showing only changes of commit 749015ff72 - Show all commits

View File

@ -1,14 +0,0 @@
# npm
/*.tgz
# Node
node_modules/
# Typescript
**/*.js
**/*.d.ts
**/tsconfig.tsbuildinfo
# PHP and Spigot Pre builds
phpOutput/
*.jar

View File

@ -38,7 +38,7 @@ jobs:
# Build Core
- name: Core Build
if: matrix.package != '@the-bds-maneger/core'
run: npm run -w "@the-bds-maneger/core" build
run: npm run -w "@the-bds-maneger/core" prepack
- name: Build "${{ matrix.package }}"
run: npm run --if-present -w "${{ matrix.package }}" prepack

View File

@ -5,91 +5,10 @@ import yargs from "yargs";
// Init yargs
yargs(process.argv.slice(2)).version(false).help(true).strictCommands().demandCommand().alias("h", "help")
.command("install", "Install server", async yargs => {
const options = yargs.option("platform", {
type: "string",
string: true,
alias: "p",
choices: ["bedrock", "java"],
default: "bedrock",
description: "Platform to install"
})
.option("altserver", {
type: "string",
string: true,
alias: "a",
choices: ["spigot", "paper", "purpur", "pocketmine", "powernukkit", "nukkit", "cloudbust"],
})
.option("id", {
alias: "i",
type: "string",
describe: "ID to update server"
})
.option("version", {
type: "string",
alias: "V",
describe: "Server version",
default: "latest"
})
.option("beta", {
type: "boolean",
boolean: true,
default: false,
describe: "allow install beta or snapshort versions"
})
.parseSync();
const serverPath = await bdsCore.serverManeger.serverManeger(options.platform === "java" ? "java" : "bedrock", {
...(options.id ? {newID: false, ID: options.id} : {newID: true}),
});
const installData = await (options.platform === "java" ? bdsCore.Java.installServer : bdsCore.Bedrock.installServer)(Object.assign({}, serverPath, {
version: options.version,
altServer: options.altserver as never,
allowBeta: Boolean(options.beta)
}));
console.log("ID: %O, Server Version: %O, Server Date: %O", installData.id, installData.version, installData.date);
})
.command("list", "list all versions", yargs => {
const { platform, altserver } = yargs.option("platform", {
type: "string",
string: true,
alias: "p",
choices: ["bedrock", "java"],
default: "bedrock",
description: "Platform to install"
}).option("altserver", {
type: "string",
string: true,
alias: "a",
choices: ["spigot", "paper", "purpur", "pocketmine", "powernukkit", "nukkit", "cloudbust"],
}).parseSync();
return (platform === "java" ? bdsCore.Java.listVersions : bdsCore.Bedrock.listVersions)(altserver as never).then(data => console.log(JSON.stringify(data, null, 2)));
})
.command("run", "Start server", async yargs => {
const option = yargs.option("id", {
type: "string",
string: true,
alias: "d",
demandOption: true,
}).parseSync();
const idInfo = (await bdsCore.listIDs()).find(local => local.id === option.id);
if (!idInfo) throw new Error("Invalid ID");
const sserverPaths = await bdsCore.serverManeger.serverManeger(option.platform === "java" ? "java" : "bedrock", {ID: option.id, newID: false});
const session = await (idInfo.platform === "java" ? bdsCore.Java.startServer : bdsCore.Bedrock.startServer)(sserverPaths);
process.on("error", console.log);
session.once("backup", filePath => console.log("Backup file path: %O", filePath));
process.stdin.pipe(session.stdin);
session.stdout.pipe(process.stdout);
session.stderr.pipe(process.stderr);
for (const ss of ([
"SIGCONT",
"SIGINT",
"SIGTERM",
] as NodeJS.Signals[])) process.on(ss, () => session.stopServer());
return session;
.command(["install", "i", "update"], "Install/update server platform", yargs => yargs, async options => {
console.log(bdsCore);
})
.command(["start", "run", "$0"], "start server", yargs => yargs, async options => {})
.parseAsync().catch(err => {
console.log(err);
process.exit(1);

View File

@ -172,4 +172,13 @@ export function getCacheVersions() {
nukkit: Array.from(nukkitCache.keys()).reduce<{[k: string]: cloudburstDownload}>((acc, key) => {acc[key] = nukkitCache.get(key); return acc;}, {}),
cloudburst: Array.from(cloudburstCache.keys()).reduce<{[k: string]: cloudburstDownload}>((acc, key) => {acc[key] = cloudburstCache.get(key); return acc;}, {}),
};
}
export async function syncCaches() {
await Promise.all([
listMojang(),
listCloudburstProject(),
listPocketmineProject(),
listPowernukkitProject()
]);
}

View File

@ -15,19 +15,26 @@ import * as bedrockVersions from "./listVersion.js";
export type platforms = "mojang" | "pocketmine" | "powernukkit" | "nukkit" | "cloudburst";
export interface bedrockPorts { }
export interface bedrockPorts {
port: number;
localListen?: string;
};
export interface playerInfo {
connected: boolean;
banned: boolean;
xuid?: string;
historic: {
action: "connected" | "spawned" | "disconnected";
action: string;
actionDate: Date;
}[];
};
class playerListen extends Map<string, playerInfo> {
constructor() { super(); }
constructor(origem?: Record<string, playerInfo>) {
super(Object.keys(origem || {}).map(key => ([key, origem[key]])));
}
toJSON() {
return Array.from(this.keys()).reduce<{ [playerName: string]: playerInfo }>((acc, player) => {
acc[player] = this.get(player);
@ -35,10 +42,20 @@ class playerListen extends Map<string, playerInfo> {
}, {});
}
init(playerName: string): this {
if (!(this.has(playerName))) {
super.set(playerName, {
banned: false,
connected: false,
historic: [],
});
}
return this;
}
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);
const playerData = this.init(playerName).get(playerName);
if (state === "disconnected") playerData.connected = false; else playerData.connected = true;
playerData.historic.push({ action: state, actionDate });
super.set(playerName, playerData);
@ -56,7 +73,8 @@ export function isBedrock(event: Bedrock<any>): event is Bedrock<any> {
export type bedrockEvents = defineEvents<{
logLine(lineString: string): void;
portListen(info: bedrockPorts): void
portListen(info: bedrockPorts): void;
installedVersion(serverVersion: string): void;
}>;
export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
@ -65,12 +83,12 @@ export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
readonly platform: P;
constructor(rootServer: string, platform: P) {
super();
if ((!(["mojang", "pocketmine", "powernukkit", "nukkit", "cloudburst"]).includes(platform))) throw new Error("Invalid platform");
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 });
}
@ -86,30 +104,35 @@ export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
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)));
this.emit("installedVersion", bedrockVersions.mojangCache.prettyVersion(version));
} 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")));
await finished((await http.streamRequest(release.url)).pipe(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 phpFile = phpFiles.sort((b, a) => a.data.Dates.Modified.getTime() - b.data.Dates.Modified.getTime()).at(0);
const binPath = path.join(this.serverFolder, "bin");
if (await extendsFS.exists(binPath)) await fs.rm(binPath, { recursive: true, force: true });
if (phpFile.name.endsWith(".tar.gz") || phpFile.name.endsWith(".tgz")) await finished((await phpFile.data.getFile()).pipe(tar.extract({ cwd: binPath })));
else if (phpFile.name.endsWith(".zip")) {
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 new Promise<void>((done, reject) => (new AdmZip(tmpFile)).extractAllToAsync(binPath, true, true, err => err ? reject(err) : done()));
await fs.rm(tmpFile, { force: true });
}
this.emit("installedVersion", bedrockVersions.pocketmineCache.prettyVersion(version));
} 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")));
this.emit("installedVersion", bedrockVersions.powernukkitCache.prettyVersion(version));
} 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")));
this.emit("installedVersion", platformVersions.prettyVersion(version));
}
}
@ -119,8 +142,74 @@ export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
serverProcess?: child_process.ChildProcess;
async runServer() {
const { platform } = this;
if (platform === "nukkit" || platform === "powernukkit" || platform === "cloudburst") {
const serverProcess = this.serverProcess = child_process.spawn("java", [
const logLine: (bedrockEvents["logLine"])[] = [];
if (platform === "mojang") {
const fileExec = path.join(this.serverFolder, (await fs.readdir(this.serverFolder)).find(file => file.startsWith("bedrock_server")));
if (process.platform !== "win32") await fs.chmod(fileExec, 0o775);
this.serverProcess = child_process.spawn(fileExec, {
cwd: this.serverFolder,
stdio: ["pipe", "pipe", "pipe"]
});
logLine.push(lineString => {
// [INFO] IPv4 supported, port: 19132
// [2023-03-08 13:01:57 INFO] Listening on IPv4 port: 19132
const ipProtocol = lineString.slice(lineString.indexOf("IPv"), lineString.indexOf("IPv")+4);
if (ipProtocol) {
let port = lineString.slice(lineString.lastIndexOf("port:")+5).trim();
if (port.indexOf(":") !== -1) port = port.slice(0, port.lastIndexOf(":"));
const info: bedrockPorts = {
port: Number(port),
localListen: ipProtocol.toLowerCase() === "ipv4" ? "0.0.0.0" : "[::]",
};
this.ports.push(info);
this.emit("portListen", info);
}
return null;
}, lineString => {
lineString = lineString.replace(/^(.*)?\[.*\]/, "").trim();
if (lineString.startsWith("Player")) {
lineString = lineString.replace("Player", "").trim();
// Spawned, disconnected, connected
let action: string;
if (lineString.startsWith("Spawned")) action = "Spawned";
else if (lineString.startsWith("disconnected")) action = "disconnected";
else if (lineString.startsWith("connected")) action = "connected";
if (!action) return null;
lineString = lineString.replace(action, "").trim();
if (lineString.startsWith(":")) lineString = lineString.slice(1).trim();
let playerName = lineString.substring(0, lineString.indexOf("xuid:")-1).trim();
if (!playerName) return null;
if (playerName.endsWith(",")) playerName = playerName.slice(0, playerName.length - 1);
let xuid: string;
if (lineString.indexOf("xuid:") !== -1) {
xuid = lineString.slice(lineString.indexOf("xuid:")+5).trim();
if (!xuid) xuid = null;
}
if (!(this.players.has(playerName))) this.players.init(playerName);
if (!!xuid) {
if (!(this.players.get(playerName)).xuid) {
const info = this.players.get(playerName);
info.xuid = xuid;
this.players.set(playerName, info);
}
}
this.players.updateState(playerName, action);
}
});
} 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 === "nukkit" || platform === "powernukkit" || platform === "cloudburst") {
this.serverProcess = child_process.spawn("java", [
"-XX:+UseG1GC",
"-XX:+ParallelRefProcEnabled",
"-XX:MaxGCPauseMillis=200",
@ -146,46 +235,35 @@ export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
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;
}
// Break lines
([
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])));
]).map(inter => inter.on("error", err => this.emit("error", err)).on("line", data => {
this.emit("logLine", data);
logLine.forEach(fn => Promise.resolve().then(() => fn(data)).catch(err => this.emit("error", err)));
}));
return this.serverProcess;
}
writeLn(data: string|Buffer) {
writeLn(data: string | Buffer) {
this.serverProcess.stdin.write(data);
if (typeof data === "string" && !(data.trim().endsWith("\n"))) this.serverProcess.stdin.write("\n");
if (Buffer.isBuffer(data) && (String.fromCharCode(data.at(-1)) !== "\n")) this.serverProcess.stdin.write("\n");
else 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})));
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") {
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"));
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
@ -195,9 +273,9 @@ export class Bedrock<P extends platforms> extends customEvent<bedrockEvents> {
}
}
async allowList(playername: string, options?: {xuid?: string, ignoresPlayerLimit?: boolean}) {
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"));
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,

View File

@ -1 +1,2 @@
export * as listVersion from "./listVersion.js";
export * as listVersion from "./listVersion.js";
export * from "./main.js";

View File

@ -1,8 +1,9 @@
import { Github, http } from "@sirherobrine23/http";
import stream from "node:stream";
import semver from "semver";
import path from "path";
import semver from "semver";
import { bdsFilesBucket } from "../../internalClouds.js";
import { versionsStorages } from "../../serverRun.js";
interface baseDownload {
URL: string;
@ -28,7 +29,7 @@ async function PromiseSplit<T extends (any[])>(arrayInput: T, fn: (value: T[numb
return result.flat(1);
}
export const mojangCache = new Map<string, mojangInfo>();
export const mojangCache = new versionsStorages<mojangInfo>();
export async function listMojang() {
const versions = (await http.jsonRequestBody<{ versions: { id: string, releaseTime: string, url: string, type: "snapshot" | "release" | "old_beta" | "old_alpha" }[] }>("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json")).versions;
await PromiseSplit(versions, async version => {
@ -48,7 +49,7 @@ export interface spigotDownload {
craftbukkit?(): Promise<stream.Readable>;
}
const spigotCache = new Map<string, spigotDownload>();
export const spigotCache = new versionsStorages<spigotDownload>();
export async function listSpigot() {
const spigotFiles = await bdsFilesBucket.listFiles("SpigotBuild/");
for (const file of spigotFiles.filter(file => file.name.slice(12).startsWith("1.")).sort((b, a) => {
@ -65,9 +66,9 @@ export async function listSpigot() {
}
}
export const paperCache = new Map<string, baseDownload>();
export const velocityCache = new Map<string, baseDownload>();
export const foliaCache = new Map<string, baseDownload>();
export const paperCache = new versionsStorages<baseDownload>();
export const velocityCache = new versionsStorages<baseDownload>();
export const foliaCache = new versionsStorages<baseDownload>();
export async function listPaperProject() {
const paperProjects = ["paper", "velocity", "folia"] as const;
await Promise.all(paperProjects.map(async projectName => {
@ -96,7 +97,7 @@ export async function listPaperProject() {
}));
}
export const purpurCache = new Map<string, baseDownload>();
export const purpurCache = new versionsStorages<baseDownload>();
export async function listPurpurProject() {
const baseURL = new URL("https://api.purpurmc.org/v2/purpur");
const { versions } = await http.jsonRequestBody<{ versions: string[] }>(baseURL);
@ -111,7 +112,7 @@ export async function listPurpurProject() {
}
}
export const glowstoneCache = new Map<string, baseDownload>();
export const glowstoneCache = new versionsStorages<baseDownload>();
export async function listGlowstoneProject() {
const repo = await Github.repositoryManeger("GlowstoneMC", "Glowstone");
const rels = (await repo.release.getRelease()).filter(rel => rel.assets.some(asset => asset.name.endsWith(".jar")));
@ -121,20 +122,14 @@ export async function listGlowstoneProject() {
})));
}
export const cuberiteCache = new Map<string, { URL: string[] }>([
[
"win32-x64",
{
URL: ["https://download.cuberite.org/windows-x86_64/Cuberite.zip"]
}
],
[
"win32-ia32",
{
URL: ["https://download.cuberite.org/windows-i386/Cuberite.zip"]
}
]
]);
export const cuberiteCache = new versionsStorages<{ URL: string[] }>({
"win32-x64": {
URL: ["https://download.cuberite.org/windows-x86_64/Cuberite.zip"]
},
"win32-ia32": {
URL: ["https://download.cuberite.org/windows-i386/Cuberite.zip"]
}
});
export async function listCuberite() {
const projects = ["android", "linux-aarch64", "linux-armhf", "linux-i386", "linux-x86_64", "darwin-x86_64"] as const;
await Promise.all(projects.map(async project => {
@ -143,7 +138,13 @@ export async function listCuberite() {
const { artifacts = [], result } = await http.jsonRequestBody<{ result: string, artifacts: { relativePath: string, fileName: string }[] }>(`https://builds.cuberite.org/job/${project}/${job.number}/api/json`);
if (result !== "SUCCESS") continue;
let map = artifacts.filter(file => !file.fileName.endsWith(".sha1")).map(file => `https://builds.cuberite.org/job/${project}/${job.number}/artifact/${file.relativePath}`);
if (project === "android") {
if (!map.length) continue;
else if (project === "darwin-x86_64") cuberiteCache.set("darwin-x64", { URL: map });
else if (project === "linux-x86_64") cuberiteCache.set("linux-x64", { URL: map });
else if (project === "linux-aarch64") cuberiteCache.set("linux-arm64", { URL: map });
else if (project === "linux-armhf") cuberiteCache.set("linux-arm", { URL: map });
else if (project === "linux-i386") cuberiteCache.set("linux-ia32", { URL: map });
else if (project === "android") {
const serverIndex = map.findIndex(file => file.toLowerCase().endsWith("server.zip"));
const server = map[serverIndex];
delete map[serverIndex];
@ -156,8 +157,19 @@ export async function listCuberite() {
else if (plat.startsWith("arm64")) cuberiteCache.set("android-arm64", { URL: [server, file] });
else if (plat.startsWith("arm")) cuberiteCache.set("android-arm", { URL: [server, file] });
}
} else cuberiteCache.set(project, { URL: map });
}
break;
}
}));
}
export async function syncCaches() {
await Promise.all([
listMojang(),
listSpigot(),
listPaperProject(),
listPurpurProject(),
listGlowstoneProject(),
listCuberite(),
]);
}

View File

@ -0,0 +1,143 @@
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 { customEvent, defineEvents } from "../../serverRun.js";
import * as javaVersions from "./listVersion.js";
export type platform = "mojang" | "spigot" | "paper" | "purpur" | "glowstone" | "folia" | "cuberite";
export type javaEvents = defineEvents<{
logLine(lineString: string): void;
}>;
/**
* Return boolean if Class input is Java class server
* @param event - Java class
* @returns
*/
export function isJava(event: Java<any>): event is Java<any> {
return event instanceof Java;
}
export class Java<P extends platform> extends customEvent<javaEvents> {
readonly serverFolder: string;
readonly rootServer: string;
readonly platform: P;
constructor(rootServer: string, platform: P) {
super();
if ((!(["mojang", "spigot", "paper", "purpur", "glowstone", "folia", "cuberite"]).includes(platform))) throw new Error("Invalid platform");
this.platform = platform;
this.rootServer = rootServer;
this.serverFolder = path.join(rootServer, "server");
Object.defineProperty(this, "rootServer", { writable: false });
Object.defineProperty(this, "serverFolder", { writable: false });
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") {
if (!javaVersions.mojangCache.size) await javaVersions.listMojang();
await finished((await http.streamRequest(javaVersions.mojangCache.get(version).URL)).pipe(createWriteStream(path.join(this.serverFolder, "server.jar"))));
} else if (platform === "spigot") {
if (!javaVersions.spigotCache.size) await javaVersions.listSpigot();
const spigotRel = javaVersions.spigotCache.get(version);
await finished((await spigotRel.getServer()).pipe(createWriteStream(path.join(this.serverFolder, "server.jar"))));
if (typeof spigotRel.craftbukkit === "function") await finished((await spigotRel.craftbukkit()).pipe(createWriteStream(path.join(this.serverFolder, "craftbukkit.jar"))));
} else if (platform === "paper") {
if (!javaVersions.paperCache.size) await javaVersions.listPaperProject();
await finished((await http.streamRequest(javaVersions.paperCache.get(version).URL)).pipe(createWriteStream(path.join(this.serverFolder, "server.jar"))));
} else if (platform === "purpur") {
if (!javaVersions.purpurCache.size) await javaVersions.listPaperProject();
await finished((await http.streamRequest(javaVersions.purpurCache.get(version).URL)).pipe(createWriteStream(path.join(this.serverFolder, "server.jar"))));
} else if (platform === "glowstone") {
if (!javaVersions.glowstoneCache.size) await javaVersions.listPaperProject();
await finished((await http.streamRequest(javaVersions.glowstoneCache.get(version).URL)).pipe(createWriteStream(path.join(this.serverFolder, "server.jar"))));
} else if (platform === "folia") {
if (!javaVersions.foliaCache.size) await javaVersions.listGlowstoneProject();
await finished((await http.streamRequest(javaVersions.foliaCache.get(version).URL)).pipe(createWriteStream(path.join(this.serverFolder, "server.jar"))));
} else if (platform === "cuberite") {
if (javaVersions.cuberiteCache.size < 3) await javaVersions.listCuberite();
const files = javaVersions.cuberiteCache.get(`${process.platform}-${process.arch}`).URL;
for (const fileURL of files) {
if (fileURL.endsWith(".zip")) {
const tmpFile = path.join(tmpdir(), Date.now() + "_" + path.basename((new URL(fileURL)).pathname));
await finished((await http.streamRequest(fileURL)).pipe(createWriteStream(tmpFile)));
await new Promise<void>((done, reject) => (new AdmZip(tmpFile)).extractAllToAsync(this.serverFolder, true, true, err => err ? reject(err) : done()));
await fs.rm(tmpFile, { force: true });
} else await finished((await http.streamRequest(fileURL)).pipe(tar.extract({cwd: this.serverFolder})));
}
}
}
serverProcess?: child_process.ChildProcess;
async runServer() {
if (this.platform === "cuberite") {
let execPath = path.join(this.serverFolder, "Cuberite");
if (process.platform === "win32") execPath += ".exe";
this.serverProcess = child_process.spawn(execPath, {
cwd: this.serverFolder,
stdio: ["pipe", "pipe", "pipe"],
});
} else {
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"],
});
}
// Break line
([
readline.createInterface(this.serverProcess.stdout),
readline.createInterface(this.serverProcess.stderr),
]).forEach(readline => readline.on("error", err => this.emit("error", err)).on("line", line => this.emit("logLine", line)));
this.on("logLine", (line) => {
line = line.replace(/^.*?\[.*\]:/, "").trim();
if (line.startsWith("Starting Minecraft server on")) {
if (line.lastIndexOf(":") === -1) return null;
// const port = Number(line.slice(line.lastIndexOf(":")+1));
// this.emit("portListen", {
// port,
// localListen: "0.0.0.0"
// });
// this.ports.push({
// port,
// localListen: "0.0.0.0"
// });
}
});
return this.serverProcess;
}
}

View File

@ -5,4 +5,5 @@ export { isBedrock } from "./platform/bedrock/index.js";
// Java platform
export * as Java from "./platform/java/index.js";
export * as java from "./platform/java/index.js";
export * as java from "./platform/java/index.js";
export { isJava } from "./platform/java/index.js";

View File

@ -1,58 +1,70 @@
import EventEmitter from "node:events";
export type EventMap = Record<string, (...args: any[]) => void>;
export type defineEvents<T extends EventMap> = T;
type EventKey<T extends EventMap> = string & keyof T;
export type defineEvents<T extends EventMap> = T;
export interface customEvent<T extends EventMap> extends EventEmitter {
emit<K extends EventKey<T>>(eventName: K, ...args: Parameters<T[K]>): boolean;
emit(name: "error", err: Error): boolean;
on<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
on(eventName: "error", fn: (err: Error) => void): this;
prependListener<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
prependListener(eventName: "error", fn: (err: Error) => void): this;
once<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
once(eventName: "error", fn: (err: Error) => void): this;
prependOnceListener<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
prependOnceListener(eventName: "error", fn: (err: Error) => void): this;
removeListener<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
removeListener(eventName: "error", fn: (err: Error) => void): this;
off<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
off(eventName: "error", fn: (err: Error) => void): this;
removeAllListeners<K extends EventKey<T>>(eventName: K): this;
removeAllListeners(eventName: "error"): this;
rawListeners<K extends EventKey<T>>(eventName: K): (T[K])[];
rawListeners(eventName: "error"): ((err: Error) => void)[];
eventNames(): (EventKey<T> | "error")[];
}
export class customEvent<T extends EventMap> extends EventEmitter {
constructor() {
super({captureRejections: true});
}
on<K extends EventKey<T>>(eventName: K, fn: T[K]): this;
on(eventName: "error", fn: (err: Error) => void): this;
on(eventName: string, fn: (...args: any) => void): this {
super.on(eventName, fn);
return this;
}
once<K extends EventKey<T>>(eventName: K, fn: T[K]): 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(name: "error", err: Error): boolean;
emit(eventName: string, ...args: any): boolean {
return super.emit(eventName, args);
}
};
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);
constructor(origem: Record<string, T> = {}) {
super(Object.keys(origem).map(key => ([key, origem[key]])));
}
prettyVersion(serverVersion: string|number): string {
const checkIsNumber = (arg0: string|number) => typeof arg0 === "number" ? arg0 : Number(arg0).toString() === arg0 ? Number(arg0) : arg0;
serverVersion = checkIsNumber(serverVersion);
if (typeof serverVersion === "number") return Array.from(this.keys()).at(serverVersion);
return serverVersion;
}
get(serverVersion: string|number): T {
return super.get(this.prettyVersion(serverVersion));
}
has(serverVersion: string|number): boolean {
return super.has(this.prettyVersion(serverVersion));
}
toJSON() {
return Array.from(super.keys()).reduce<{[version: string]: T}>((acc, key) => {
acc[key] = super.get(key);
return acc;
}, {});
}
}

View File

@ -1,12 +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();
const server = new Bedrock(path.join(homedir(), ".bdsmaneger/playgroud/mojang"), "pocketmine");
server.once("installedVersion", version => console.log("Installed %s server", version));
await listVersion.listPocketmineProject();
console.log("Init install");
await server.installServer(0);
server.on("logLine", (line) => console.log(line[0]));
const pr = await server.runServer();
process.stdin.pipe(pr.stdin);

View File

@ -0,0 +1,11 @@
import path from "node:path";
import { Java } from "../src/platform/java/index.js";
import { homedir } from "node:os";
import { rm } from "node:fs/promises";
await rm(path.join(homedir(), ".bdsmaneger/playgroud/java"), { recursive: true }).catch(() => {});
const server = new Java(path.join(homedir(), ".bdsmaneger/playgroud/java"), "cuberite");
await server.installServer(0);
server.on("logLine", line => console.log(line));
const run = await server.runServer();
process.stdin.pipe(run.stdin);

View File

@ -0,0 +1,7 @@
# Node
node_modules/
# Typescript
**/*.js
**/*.d.ts
**/*.tsbuildinfo

View File

@ -1,133 +1,186 @@
#!/usr/bin/env node
import { Java, Bedrock } from "@the-bds-maneger/core";
import { createServer } from "node:http";
import { Bedrock, Java } from "@the-bds-maneger/core";
import express from "express";
import expressLayer from "express/lib/router/layer.js";
import { createServer } from "node:http";
import { format } from "node:util";
process.on("unhandledRejection", err => console.error(err));
expressLayer.prototype.handle_request = async function handle_request_promised(...args) {
var fn = this.handle;
if (fn.length > 3) return args.at(-1)();
await Promise.resolve().then(() => fn.call(this, ...args)).catch(args.at(-1));
}
/**
* @param {number} nextTime
*/
function printDate(nextTime = 0) {
const dd = new Date(Date.now() + nextTime);
return format("%f/%f/%f %f:%f:%f", dd.getDate(), dd.getMonth() + 1, dd.getFullYear(), dd.getMinutes(), dd.getHours(), dd.getSeconds());
}
const interval = 1000 * 60 * 60 * 2;
console.log("Initial versions");
await Promise.all([Bedrock.listVersion.syncCaches(), Java.listVersion.syncCaches()]);
console.log("Next sync in", printDate(interval));
setInterval(async () => {
console.log("Sync versions");
await Promise.all([Bedrock.listVersion.syncCaches(), Java.listVersion.syncCaches()]);
console.log("Next sync in", printDate(interval));
}, interval);
const app = express();
app.use((req, res, next) => {
if (!(req.query.pretty === "off" || req.query.pretty === "false")) {
res.json = (body) => res.setHeader("Content-Type", "application/json").send(JSON.stringify(body, null, 2));
}
next();
});
const server = createServer(app);
server.listen(Number(process.env.PORT || 3000), () => {
const addr = server.address();
console.log("Listening on http://localhost:%f", Number(typeof addr === "object" ? addr.port : addr));
if (typeof addr === "string") console.log("Server listen on socket %O path", addr);
else if (typeof addr === "number") console.log("Listening on http://localhost:%f", addr);
else if (typeof addr === "object") console.log("Listen on http://localhost:%s", addr?.port);
});
// Bedrock
const bedrockRoute = express.Router();
app.use("/bedrock", bedrockRoute);
const bedrockStash = {};
bedrockRoute.get("/", async ({res}) => {
const bedrockOficial = (await (async () => {if (bedrockStash["oficial"]) return bedrockStash["oficial"];bedrockStash["oficial"] = await Bedrock.listVersions();setTimeout(() => delete bedrockStash["oficial"], 60000);return bedrockStash["oficial"];})()).filter(rel => rel.release === "stable").at(0);
const cloudbust = (await (async () => {if (bedrockStash["cloudbust"]) return bedrockStash["cloudbust"];bedrockStash["cloudbust"] = await Bedrock.listVersions("cloudbust");setTimeout(() => delete bedrockStash["cloudbust"], 60000);return bedrockStash["cloudbust"];})()).at(0);
const nukkit = (await (async () => {if (bedrockStash["nukkit"]) return bedrockStash["nukkit"];bedrockStash["nukkit"] = await Bedrock.listVersions("nukkit");setTimeout(() => delete bedrockStash["nukkit"], 60000);return bedrockStash["nukkit"];})()).at(0);
const powernukkit = (await (async () => {if (bedrockStash["powernukkit"]) return bedrockStash["powernukkit"];bedrockStash["powernukkit"] = await Bedrock.listVersions("powernukkit");setTimeout(() => delete bedrockStash["powernukkit"], 60000);return bedrockStash["powernukkit"];})()).at(0);
const pocketmine = (await (async () => {if (bedrockStash["pocketmine"]) return bedrockStash["pocketmine"];bedrockStash["pocketmine"] = await Bedrock.listVersions("pocketmine");setTimeout(() => delete bedrockStash["pocketmine"], 60000);return bedrockStash["pocketmine"];})()).filter(rel => rel.release === "stable").at(0);
bedrockRoute.get("/", async ({ res }) => {
return res.json({
bedrockOficial,
pocketmine,
cloudbust,
nukkit,
powernukkit
bedrockOficial: Bedrock.listVersion.mojangCache,
pocketmine: Bedrock.listVersion.pocketmineCache,
cloudbust: Bedrock.listVersion.cloudburstCache,
nukkit: Bedrock.listVersion.nukkitCache,
powernukkit: Bedrock.listVersion.powernukkitCache
});
});
bedrockRoute.get("/((oficial|cloudbust|nukkit|nukkit|powernukkit|pocketmine))", async (req, res) => {
let platform = req.params[0];
if (!bedrockStash[platform]) {
if (platform === "oficial") platform = null;
bedrockStash[(platform === null ? "oficial" : platform)] = await Bedrock.listVersions(platform);
setTimeout(() => delete bedrockStash[(platform === null ? "oficial" : platform)], 60000);
}
const list = bedrockStash[(platform === null ? "oficial" : platform)];
/** @type {"oficial" | "cloudbust" | "nukkit" | "nukkit" | "powernukkit" | "pocketmine"} */
const platform = req.params[0];
const ver = String(req.query.v || req.query.version || "");
if (ver) {
const data = list.find(e => e.version === ver);
if (!data) return res.status(400).json({error: "Not found version."});
return res.json(data);
if (platform === "oficial") {
if (!ver) return res.json(Bedrock.listVersion.mojangCache.toJSON());
else {
if (!(Bedrock.listVersion.mojangCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Bedrock.listVersion.mojangCache.get(ver));
}
} else if (platform === "pocketmine") {
if (!ver) return res.json(Bedrock.listVersion.pocketmineCache.toJSON());
else {
if (!(Bedrock.listVersion.pocketmineCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Bedrock.listVersion.pocketmineCache.get(ver));
}
} else if (platform === "nukkit") {
if (!ver) return res.json(Bedrock.listVersion.nukkitCache.toJSON());
else {
if (!(Bedrock.listVersion.nukkitCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Bedrock.listVersion.nukkitCache.get(ver));
}
} else if (platform === "cloudbust") {
if (!ver) return res.json(Bedrock.listVersion.cloudburstCache.toJSON());
else {
if (!(Bedrock.listVersion.cloudburstCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Bedrock.listVersion.cloudburstCache.get(ver));
}
} else if (platform === "powernukkit") {
if (!ver) return res.json(Bedrock.listVersion.powernukkitCache.toJSON());
else {
if (!(Bedrock.listVersion.powernukkitCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Bedrock.listVersion.powernukkitCache.get(ver));
}
}
return res.json(list);
});
// Java
const javaRoute = express.Router();
app.use("/java", javaRoute);
const javaStash = {};
javaRoute.get("/", async ({res}) => {
const javaOficial = (await (async () => {if (javaStash["oficial"]) return javaStash["oficial"]; javaStash["oficial"] = await Java.listVersions(); setTimeout(() => delete javaStash["oficial"], 6000); return javaStash["oficial"];})()).filter(rel => rel.release === "stable").at(0);
const spigot = (await (async () => {if (javaStash["spigot"]) return javaStash["spigot"]; javaStash["spigot"] = await Java.listVersions("spigot"); setTimeout(() => delete javaStash["spigot"], 6000); return javaStash["spigot"];})()).at(0);
const paper = (await (async () => {if (javaStash["paper"]) return javaStash["paper"]; javaStash["paper"] = await Java.listVersions("paper"); setTimeout(() => delete javaStash["paper"], 6000); return javaStash["paper"];})()).at(0);
const glowstone = (await (async () => {if (javaStash["glowstone"]) return javaStash["glowstone"]; javaStash["glowstone"] = await Java.listVersions("glowstone"); setTimeout(() => delete javaStash["glowstone"], 6000); return javaStash["glowstone"];})()).at(0);
const purpur = (await (async () => {if (javaStash["purpur"]) return javaStash["purpur"]; javaStash["purpur"] = await Java.listVersions("purpur"); setTimeout(() => delete javaStash["purpur"], 6000); return javaStash["purpur"];})()).at(0);
const folia = (await (async () => {if (javaStash["folia"]) return javaStash["folia"]; javaStash["folia"] = await Java.listVersions("folia"); setTimeout(() => delete javaStash["folia"], 6000); return javaStash["folia"];})()).at(0);
const cuberite = (await (async () => {if (javaStash["cuberite"]) return javaStash["cuberite"]; javaStash["cuberite"] = await Java.listVersions("cuberite"); setTimeout(() => delete javaStash["cuberite"], 6000); return javaStash["cuberite"];})()).at(0);
javaRoute.get("/", async ({ res }) => {
return res.json({
javaOficial,
spigot,
paper,
glowstone,
purpur,
folia,
cuberite
javaOficial: Java.listVersion.mojangCache,
spigot: Java.listVersion.spigotCache,
paper: Java.listVersion.paperCache,
glowstone: Java.listVersion.glowstoneCache,
purpur: Java.listVersion.purpurCache,
folia: Java.listVersion.foliaCache,
cuberite: Java.listVersion.cuberiteCache
});
});
javaRoute.get("/((oficial|spigot|paper|purpur|glowstone|folia|cuberite))", async (req, res) => {
let platform = req.params[0];
if (!javaStash[platform]) {
if (platform === "oficial") platform = null;
javaStash[(platform === null ? "oficial" : platform)] = await Java.listVersions(platform);
setTimeout(() => delete javaStash[(platform === null ? "oficial" : platform)], 60000);
}
const list = javaStash[(platform === null ? "oficial" : platform)];
/** @type {"oficial" | "spigot" | "paper" | "purpur" | "glowstone" | "folia" | "cuberite"} */
const platform = req.params[0];
const ver = String(req.query.v || req.query.version || "");
if (ver) {
const data = list.find(e => e.version === ver);
if (!data) return res.status(400).json({error: "Not found version."});
return res.json(data);
if (platform === "oficial") {
if (!ver) return res.json(Java.listVersion.mojangCache.toJSON());
else {
if (!(Java.listVersion.mojangCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.mojangCache.get(ver));
}
} else if (platform === "spigot") {
if (!ver) return res.json(Java.listVersion.spigotCache.toJSON());
else {
if (!(Java.listVersion.spigotCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.spigotCache.get(ver));
}
} else if (platform === "paper") {
if (!ver) return res.json(Java.listVersion.paperCache.toJSON());
else {
if (!(Java.listVersion.paperCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.paperCache.get(ver));
}
} else if (platform === "purpur") {
if (!ver) return res.json(Java.listVersion.purpurCache.toJSON());
else {
if (!(Java.listVersion.purpurCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.purpurCache.get(ver));
}
} else if (platform === "glowstone") {
if (!ver) return res.json(Java.listVersion.glowstoneCache.toJSON());
else {
if (!(Java.listVersion.glowstoneCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.glowstoneCache.get(ver));
}
} else if (platform === "cuberite") {
if (!ver) return res.json(Java.listVersion.cuberiteCache.toJSON());
else {
if (!(Java.listVersion.cuberiteCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.cuberiteCache.get(ver));
}
} else if (platform === "folia") {
if (!ver) return res.json(Java.listVersion.foliaCache.toJSON());
else {
if (!(Java.listVersion.foliaCache.has(ver))) return res.status(400).json({error: "This version not exists!"});
return res.json(Java.listVersion.foliaCache.get(ver));
}
}
return res.json(list);
});
app.get("/", async ({res}) => {
// Bedrock
const bedrockOficial = (await (async () => {if (bedrockStash["oficial"]) return bedrockStash["oficial"];bedrockStash["oficial"] = await Bedrock.listVersions();setTimeout(() => delete bedrockStash["oficial"], 60000);return bedrockStash["oficial"];})()).filter(rel => rel.release === "stable").at(0);
const cloudbust = (await (async () => {if (bedrockStash["cloudbust"]) return bedrockStash["cloudbust"];bedrockStash["cloudbust"] = await Bedrock.listVersions("cloudbust");setTimeout(() => delete bedrockStash["cloudbust"], 60000);return bedrockStash["cloudbust"];})()).at(0);
const nukkit = (await (async () => {if (bedrockStash["nukkit"]) return bedrockStash["nukkit"];bedrockStash["nukkit"] = await Bedrock.listVersions("nukkit");setTimeout(() => delete bedrockStash["nukkit"], 60000);return bedrockStash["nukkit"];})()).at(0);
const powernukkit = (await (async () => {if (bedrockStash["powernukkit"]) return bedrockStash["powernukkit"];bedrockStash["powernukkit"] = await Bedrock.listVersions("powernukkit");setTimeout(() => delete bedrockStash["powernukkit"], 60000);return bedrockStash["powernukkit"];})()).at(0);
const pocketmine = (await (async () => {if (bedrockStash["pocketmine"]) return bedrockStash["pocketmine"];bedrockStash["pocketmine"] = await Bedrock.listVersions("pocketmine");setTimeout(() => delete bedrockStash["pocketmine"], 60000);return bedrockStash["pocketmine"];})()).filter(rel => rel.release === "stable").at(0);
// Java
const javaOficial = (await (async () => {if (javaStash["oficial"]) return javaStash["oficial"]; javaStash["oficial"] = await Java.listVersions(); setTimeout(() => delete javaStash["oficial"], 6000); return javaStash["oficial"];})()).filter(rel => rel.release === "stable").at(0);
const spigot = (await (async () => {if (javaStash["spigot"]) return javaStash["spigot"]; javaStash["spigot"] = await Java.listVersions("spigot"); setTimeout(() => delete javaStash["spigot"], 6000); return javaStash["spigot"];})()).at(0);
const paper = (await (async () => {if (javaStash["paper"]) return javaStash["paper"]; javaStash["paper"] = await Java.listVersions("paper"); setTimeout(() => delete javaStash["paper"], 6000); return javaStash["paper"];})()).at(0);
const glowstone = (await (async () => {if (javaStash["glowstone"]) return javaStash["glowstone"]; javaStash["glowstone"] = await Java.listVersions("glowstone"); setTimeout(() => delete javaStash["glowstone"], 6000); return javaStash["glowstone"];})()).at(0);
const purpur = (await (async () => {if (javaStash["purpur"]) return javaStash["purpur"]; javaStash["purpur"] = await Java.listVersions("purpur"); setTimeout(() => delete javaStash["purpur"], 6000); return javaStash["purpur"];})()).at(0);
const folia = (await (async () => {if (javaStash["folia"]) return javaStash["folia"]; javaStash["folia"] = await Java.listVersions("folia"); setTimeout(() => delete javaStash["folia"], 6000); return javaStash["folia"];})()).at(0);
const cuberite = (await (async () => {if (javaStash["cuberite"]) return javaStash["cuberite"]; javaStash["cuberite"] = await Java.listVersions("cuberite"); setTimeout(() => delete javaStash["cuberite"], 6000); return javaStash["cuberite"];})()).at(0);
app.get("/", async ({ res }) => {
return res.json({
bedrock: {
bedrockOficial,
pocketmine,
cloudbust,
nukkit,
powernukkit
bedrockOficial: Bedrock.listVersion.mojangCache,
pocketmine: Bedrock.listVersion.pocketmineCache,
cloudbust: Bedrock.listVersion.cloudburstCache,
nukkit: Bedrock.listVersion.nukkitCache,
powernukkit: Bedrock.listVersion.powernukkitCache
},
java: {
javaOficial,
spigot,
paper,
glowstone,
purpur,
folia,
cuberite
javaOficial: Java.listVersion.mojangCache,
spigot: Java.listVersion.spigotCache,
paper: Java.listVersion.paperCache,
glowstone: Java.listVersion.glowstoneCache,
purpur: Java.listVersion.purpurCache,
folia: Java.listVersion.foliaCache,
cuberite: Java.listVersion.cuberiteCache
}
})
});
// 404
app.use(({res}) => res.status(404).json({error: "Not found page."}));
app.use((error, _req, res, _next) => res.status(500).json({error: error?.message || String(error) || "Unknown error."}));
app.use(({ res }) => res.status(404).json({ error: "Not found page." }));
app.use((error, _req, res, _next) => res.status(500).json({ error: error?.message || String(error) || "Unknown error." }));

9
railway.json Normal file
View File

@ -0,0 +1,9 @@
{
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "packages/verapiDockerfile"
},
"deploy": {
"restartPolicyType": "ALWAYS"
}
}