diff options
Diffstat (limited to 'cli/commands/generate.js')
| -rw-r--r-- | cli/commands/generate.js | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/cli/commands/generate.js b/cli/commands/generate.js new file mode 100644 index 0000000..183810d --- /dev/null +++ b/cli/commands/generate.js @@ -0,0 +1,237 @@ +const fs = require("fs-extra") +const path = require("path") +const { spawn } = require("child_process") + +const { LoadConfig } = require("../../lib/config") +const { GetModVersions, GetCompatibleVersion, DownloadFile } = require("../../lib/modrinth") + +const config = LoadConfig() + +const +{ + minecraft_version, + fabric_loader_version, + loader_meta_version, + java_path, + modrinth_mods +} = config + +const javaCmd = java_path || "java" + +const serverDir = path.join(__dirname, "../../server") +const modsDir = path.join(serverDir, "mods") + +const deleteList = +[ + "audioplayer_uploads", + "config", + "cache", + "world", + "logs", + "banned-players.json", + "server.properties", + "banned-ips.json", + "usercache.json", + "whitelist.json", + "ops.json", + "eula.txt" +] + +const EnsureDirs = async () => +{ + await fs.ensureDir(serverDir) + await fs.ensureDir(modsDir) +} + +const DownloadModrinthMods = async (modList, mcVersion) => +{ + const downloadedFiles = [] + const failedMods = [] + + for (const slug of modList) + { + try + { + const versions = await GetModVersions(slug) + if (!versions) + { + console.log(` [Modrinth] Could not get versions for ${slug}`) + failedMods.push(slug) + continue + } + + const compatible = GetCompatibleVersion(versions, mcVersion, "fabric") + if (!compatible) + { + console.log(` [Modrinth] No Fabric version for ${slug} on MC ${mcVersion}`) + failedMods.push(slug) + continue + } + + const fileInfo = compatible.files[0] + const jarUrl = fileInfo.url + const filename = fileInfo.filename + const destPath = path.join(modsDir, filename) + + console.log(` [Modrinth] Downloading ${slug} -> ${filename} ...`) + + const success = await DownloadFile(jarUrl, destPath) + if (!success) + { + console.log(` [Modrinth] Couldn't download jar for ${slug}`) + failedMods.push(slug) + continue + } + + downloadedFiles.push(destPath) + console.log(` [Modrinth] Downloaded: ${filename}`) + } + catch (err) + { + console.error(` [Modrinth] Error downloading ${slug}:`, err.message) + failedMods.push(slug) + } + } + + return { downloadedFiles, failedMods } +} + +const DownloadFabricServer = async (mcVersion, loaderVersion, metaVersion) => +{ + const url = `https://meta.fabricmc.net/v2/versions/loader/${mcVersion}/${loaderVersion}/${metaVersion}/server/jar` + const filename = "server.jar" + const destPath = path.join(serverDir, filename) + + console.log(` [Fabric] Downloading Fabric server for MC ${mcVersion} ...`) + + const success = await DownloadFile(url, destPath) + if (!success) + { + console.log(` [Fabric] Failed to download Fabric server JAR`) + return null + } + + console.log(` [Fabric] Downloaded: ${filename}`) + return destPath +} + +const RunServerAutoStop = (rl) => +{ + return new Promise((resolve) => + { + const jarPath = path.join(serverDir, "server.jar") + + if (!fs.existsSync(jarPath)) + { + console.log(" Server JAR not found.") + resolve(false) + return + } + + const eulaPath = path.join(serverDir, "eula.txt") + if (!fs.existsSync(eulaPath)) + { + console.log(" Accepting EULA...") + fs.writeFileSync(eulaPath, "eula=true\n") + } + + console.log(" Starting server (will auto-stop after loading)...\n") + + rl.pause() + + let hadError = false + + const serverProcess = spawn(javaCmd, ["-jar", "server.jar", "nogui"], + { + cwd: serverDir, + stdio: ["pipe", "pipe", "pipe"] + }) + + serverProcess.stdout.on("data", (data) => + { + const line = data.toString() + process.stdout.write(line) + + if (line.includes("Done")) + { + console.log("\n Server loaded, stopping...") + serverProcess.stdin.write("stop\n") + } + }) + + serverProcess.stderr.on("data", (data) => + { + process.stderr.write(data.toString()) + }) + + serverProcess.on("error", err => + { + hadError = true + console.error(" Failed to start server:", err.message) + rl.resume() + resolve(false) + }) + + serverProcess.on("close", () => + { + if (!hadError) + { + console.log("\n Server stopped.") + rl.resume() + resolve(true) + } + }) + }) +} + +const DeletePostGenerate = async () => +{ + console.log(" Cleaning up server-generated files...") + + let removed = 0 + for (const item of deleteList) + { + const itemPath = path.join(serverDir, item) + + try + { + if (await fs.pathExists(itemPath)) + { + await fs.remove(itemPath) + removed++ + } + } + catch (err) + { + console.error(` Failed to remove ${item}:`, err.message) + } + } + + console.log(` Removed ${removed} items.`) +} + +const Generate = async (rl) => +{ + await EnsureDirs() + + console.log(`\n Generating server for MC ${minecraft_version}...\n`) + + const { downloadedFiles, failedMods } = await DownloadModrinthMods(modrinth_mods, minecraft_version) + console.log(`\n [OK] Downloaded ${downloadedFiles.length} mods`) + if (failedMods.length) + console.log(` [FAIL] Failed: ${failedMods.join(", ")}`) + + const serverFile = await DownloadFabricServer(minecraft_version, fabric_loader_version, loader_meta_version) + + if (serverFile) + { + console.log("") + await RunServerAutoStop(rl) + await DeletePostGenerate() + } + + console.log("") +} + +module.exports = { Generate, serverDir } + |
