Spaces:
Sleeping
Sleeping
Download backend/auto-setup.js from techedstudios/smp-manager: direct link, hf CLI and curl.
- Browser
- Download file 9.2 kB
-
https://huggingface.co/spaces/techedstudios/smp-manager/resolve/main/backend/auto-setup.js
- Command line
-
hf download hf://spaces/techedstudios/smp-manager/backend/auto-setup.js
-
curl -L -o auto-setup.js https://huggingface.co/spaces/techedstudios/smp-manager/resolve/main/backend/auto-setup.js
9.2 kB
| import net from 'net'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| import AdmZip from 'adm-zip'; | |
| // Find a free TCP port starting at a specific port | |
| export async function findFreePort(startPort = 25565) { | |
| let port = startPort; | |
| while (true) { | |
| const isFree = await new Promise((resolve) => { | |
| const server = net.createServer(); | |
| server.once('error', () => { | |
| resolve(false); | |
| }); | |
| server.once('listening', () => { | |
| server.close(); | |
| resolve(true); | |
| }); | |
| server.listen(port); | |
| }); | |
| if (isFree) { | |
| return port; | |
| } | |
| port++; | |
| } | |
| } | |
| // Download file helper | |
| async function downloadFile(url, destPath, progressCallback) { | |
| const response = await fetch(url); | |
| if (!response.ok) { | |
| throw new Error(`Failed to download from ${url}: ${response.statusText}`); | |
| } | |
| const contentLength = response.headers.get('content-length'); | |
| const totalBytes = contentLength ? parseInt(contentLength, 10) : 0; | |
| const fileStream = fs.createWriteStream(destPath); | |
| const reader = response.body.getReader(); | |
| let downloadedBytes = 0; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| fileStream.write(value); | |
| downloadedBytes += value.length; | |
| if (totalBytes && progressCallback) { | |
| const pct = Math.round((downloadedBytes / totalBytes) * 100); | |
| progressCallback(pct); | |
| } | |
| } | |
| fileStream.end(); | |
| return new Promise((resolve, reject) => { | |
| fileStream.on('finish', resolve); | |
| fileStream.on('error', reject); | |
| }); | |
| } | |
| // Download PaperMC Jar | |
| export async function downloadPaper(version = '1.21', destDir, progressCallback) { | |
| const apiBase = 'https://api.papermc.io/v2/projects/paper'; | |
| // Get latest build | |
| const buildInfoResponse = await fetch(`${apiBase}/versions/${version}`); | |
| if (!buildInfoResponse.ok) { | |
| throw new Error(`Unsupported Minecraft version: ${version}`); | |
| } | |
| const buildInfo = await buildInfoResponse.json(); | |
| const builds = buildInfo.builds; | |
| const latestBuild = builds[builds.length - 1]; | |
| // Get jar file name | |
| const jarResponse = await fetch(`${apiBase}/versions/${version}/builds/${latestBuild}`); | |
| const jarInfo = await jarResponse.json(); | |
| const jarName = jarInfo.downloads.application.name; | |
| const downloadUrl = `${apiBase}/versions/${version}/builds/${latestBuild}/downloads/${jarName}`; | |
| const destPath = path.join(destDir, 'server.jar'); | |
| fs.mkdirSync(destDir, { recursive: true }); | |
| await downloadFile(downloadUrl, destPath, progressCallback); | |
| return destPath; | |
| } | |
| // Download Velocity Proxy Jar | |
| export async function downloadVelocity(version = '3.3.0-SNAPSHOT', destDir, progressCallback) { | |
| const apiBase = 'https://api.papermc.io/v2/projects/velocity'; | |
| let buildInfoResponse = await fetch(`${apiBase}/versions/${version}`); | |
| if (!buildInfoResponse.ok) { | |
| version = '3.3.0-SNAPSHOT'; | |
| buildInfoResponse = await fetch(`${apiBase}/versions/${version}`); | |
| } | |
| const buildInfo = await buildInfoResponse.json(); | |
| const builds = buildInfo.builds; | |
| const latestBuild = builds[builds.length - 1]; | |
| const jarResponse = await fetch(`${apiBase}/versions/${version}/builds/${latestBuild}`); | |
| const jarInfo = await jarResponse.json(); | |
| const jarName = jarInfo.downloads.application.name; | |
| const downloadUrl = `${apiBase}/versions/${version}/builds/${latestBuild}/downloads/${jarName}`; | |
| const destPath = path.join(destDir, 'velocity.jar'); | |
| fs.mkdirSync(destDir, { recursive: true }); | |
| await downloadFile(downloadUrl, destPath, progressCallback); | |
| return destPath; | |
| } | |
| // Download Purpur Jar | |
| export async function downloadPurpur(version = '1.21', destDir, progressCallback) { | |
| const downloadUrl = `https://api.purpurmc.org/v2/purpur/${version}/latest/download`; | |
| const destPath = path.join(destDir, 'server.jar'); | |
| fs.mkdirSync(destDir, { recursive: true }); | |
| await downloadFile(downloadUrl, destPath, progressCallback); | |
| return destPath; | |
| } | |
| // Download Bedrock Dedicated Server ZIP and extract it | |
| export async function downloadBedrock(destDir, progressCallback) { | |
| // Use a stable, official CDN URL for Windows Bedrock Server (1.20.81.01 as default stable fallback) | |
| const downloadUrl = 'https://minecraft.azureedge.net/bin-win/bedrock-server-1.20.81.01.zip'; | |
| const zipPath = path.join(destDir, 'bedrock-server.zip'); | |
| fs.mkdirSync(destDir, { recursive: true }); | |
| // Download the ZIP | |
| await downloadFile(downloadUrl, zipPath, progressCallback); | |
| // Extract using adm-zip | |
| const zip = new AdmZip(zipPath); | |
| zip.extractAllTo(destDir, true); | |
| // Delete the ZIP file after extraction | |
| fs.unlinkSync(zipPath); | |
| return path.join(destDir, 'bedrock_server.exe'); | |
| } | |
| // Download Fabric Server Jar | |
| export async function downloadFabric(version = '1.21', destDir, progressCallback) { | |
| // Get latest loader version | |
| const loaderRes = await fetch('https://meta.fabricmc.net/v2/versions/loader'); | |
| const loaders = await loaderRes.json(); | |
| const loaderVersion = loaders[0].version; | |
| // Get latest installer version | |
| const installerRes = await fetch('https://meta.fabricmc.net/v2/versions/installer'); | |
| const installers = await installerRes.json(); | |
| const installerVersion = installers[0].version; | |
| const downloadUrl = `https://meta.fabricmc.net/v2/versions/loader/${version}/${loaderVersion}/${installerVersion}/server/jar`; | |
| const destPath = path.join(destDir, 'fabric-server-launch.jar'); | |
| fs.mkdirSync(destDir, { recursive: true }); | |
| await downloadFile(downloadUrl, destPath, progressCallback); | |
| return destPath; | |
| } | |
| // Download FRPC binary | |
| import os from 'os'; | |
| export async function downloadFrpc(destDir, progressCallback) { | |
| const platform = os.platform(); | |
| const arch = os.arch(); | |
| let releaseArch = 'amd64'; | |
| if (arch === 'arm64') releaseArch = 'arm64'; | |
| let releaseOs = 'linux'; | |
| let ext = 'tar.gz'; | |
| let binName = 'frpc'; | |
| if (platform === 'win32') { | |
| releaseOs = 'windows'; | |
| ext = 'zip'; | |
| binName = 'frpc.exe'; | |
| } else if (platform === 'darwin') { | |
| releaseOs = 'darwin'; | |
| } | |
| const version = '0.58.1'; | |
| const folderName = `frp_${version}_${releaseOs}_${releaseArch}`; | |
| const fileName = `${folderName}.${ext}`; | |
| const downloadUrl = `https://github.com/fatedier/frp/releases/download/v${version}/${fileName}`; | |
| const archivePath = path.join(destDir, fileName); | |
| fs.mkdirSync(destDir, { recursive: true }); | |
| // Check if already exists | |
| const finalBinPath = path.join(destDir, binName); | |
| if (fs.existsSync(finalBinPath)) { | |
| if (progressCallback) progressCallback(100); | |
| return finalBinPath; | |
| } | |
| await downloadFile(downloadUrl, archivePath, progressCallback); | |
| if (ext === 'zip') { | |
| const zip = new AdmZip(archivePath); | |
| zip.extractAllTo(destDir, true); | |
| // move the binary out of the extracted folder | |
| fs.renameSync(path.join(destDir, folderName, binName), finalBinPath); | |
| fs.rmSync(path.join(destDir, folderName), { recursive: true, force: true }); | |
| } else { | |
| // For tar.gz on Linux, we can use spawn 'tar' | |
| const { spawnSync } = await import('child_process'); | |
| spawnSync('tar', ['-xzf', archivePath, '-C', destDir]); | |
| fs.renameSync(path.join(destDir, folderName, binName), finalBinPath); | |
| fs.rmSync(path.join(destDir, folderName), { recursive: true, force: true }); | |
| // make it executable | |
| fs.chmodSync(finalBinPath, 0o755); | |
| } | |
| fs.unlinkSync(archivePath); | |
| return finalBinPath; | |
| } | |
| // Auto-download ViaVersion suite for cross-play | |
| export async function downloadViaPlugins(serverPath, software, onLog) { | |
| const pluginsDir = path.join(serverPath, software === 'Fabric' ? 'mods' : 'plugins'); | |
| fs.mkdirSync(pluginsDir, { recursive: true }); | |
| const loader = software === 'Fabric' ? 'fabric' : 'paper'; | |
| const projects = ['viaversion', 'viabackwards', 'viarewind', 'geyser', 'floodgate']; | |
| for (const project of projects) { | |
| try { | |
| onLog(`[SYSTEM] Resolving latest ${project} for ${loader}...`); | |
| const res = await fetch(`https://api.modrinth.com/v2/project/${project}/version`, { headers: { 'User-Agent': 'techedstudios/mc-hoster' } }); | |
| if (!res.ok) continue; | |
| const versions = await res.json(); | |
| if (!versions || versions.length === 0) continue; | |
| let best = versions.find(v => v.loaders && v.loaders.includes(loader)) || versions[0]; | |
| const file = best.files.find(f => f.primary) || best.files[0]; | |
| if (!file) continue; | |
| const destPath = path.join(pluginsDir, file.filename); | |
| // Remove old version if exists | |
| const existing = fs.readdirSync(pluginsDir).find(f => f.toLowerCase().startsWith(project.replace('-', '')) || f.toLowerCase().startsWith(project)); | |
| if (existing && existing !== file.filename) { | |
| fs.unlinkSync(path.join(pluginsDir, existing)); | |
| } | |
| if (fs.existsSync(destPath)) { | |
| onLog(`[SYSTEM] ${file.filename} already installed.`); | |
| continue; | |
| } | |
| onLog(`[SYSTEM] Downloading ${file.filename}...`); | |
| await downloadFile(file.url, destPath, null); | |
| onLog(`[SYSTEM] Installed ${project}.`); | |
| } catch (e) { | |
| onLog(`[SYSTEM] Failed to download ${project}: ${e.message}`); | |
| } | |
| } | |
| } | |