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 }