hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/cli/commands/generate.js
diff options
context:
space:
mode:
Diffstat (limited to 'cli/commands/generate.js')
-rw-r--r--cli/commands/generate.js237
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 }
+