2024-08-04 22:23:17 +10:00

274 lines
6.5 KiB
TypeScript

import upath from "upath";
import unzip from "unzipper";
import through from "through2";
import mustache from "mustache";
import { src, dest, series } from "gulp";
import fs from "fs";
import buildConfig from "#buildConfig";
import { ForgeProfile } from "#types/forgeProfile.ts";
import { FileDef } from "#types/fileDef.ts";
import {
downloadOrRetrieveFileDef,
getForgeJar,
getVersionManifest,
} from "#utils/util.ts";
import {
modDestDirectory,
modpackManifest,
serverDestDirectory,
sharedDestDirectory,
} from "#globals";
import { deleteAsync } from "del";
import logInfo, { logWarn } from "#utils/log.ts";
let g_forgeJar: string | undefined = undefined;
async function serverCleanUp() {
return deleteAsync(upath.join(serverDestDirectory, "*"), { force: true });
}
/**
* Checks and creates all necessary directories so we can build the server safely.
*/
async function createServerDirs() {
if (!fs.existsSync(serverDestDirectory)) {
return fs.promises.mkdir(serverDestDirectory, { recursive: true });
}
}
/**
* Download the Forge jar.
*
* Extract, parse the profile data and download required libraries.
*/
async function downloadForge() {
const { forgeJar, forgeUniversalPath } = await getForgeJar();
/**
* Parse the profile manifest.
*/
let forgeUniversalJar: Buffer | undefined = undefined;
let forgeProfile: ForgeProfile | undefined = undefined;
const files = (await unzip.Open.buffer(forgeJar))?.files;
logInfo("Extracting Forge installation profile & jar...");
if (!files) {
throw new Error("Malformed Forge installation jar.");
}
for (const file of files) {
// Look for the universal jar.
if (!forgeUniversalJar && file.path == forgeUniversalPath) {
forgeUniversalJar = await file.buffer();
}
// Look for the installation profile.
else if (!forgeProfile && file.path == "version.json") {
forgeProfile = JSON.parse((await file.buffer()).toString());
}
if (forgeUniversalJar && forgeProfile) {
break;
}
}
if (!forgeProfile || !forgeProfile.libraries) {
throw new Error("Malformed Forge installation profile.");
}
if (!forgeUniversalJar) {
throw new Error(
"Couldn't find the universal Forge jar in the installation jar.",
);
}
/**
* Move the universal jar into the dist folder.
*/
logInfo("Extracting the Forge jar...");
await fs.promises.writeFile(
upath.join(serverDestDirectory, upath.basename(forgeUniversalPath)),
forgeUniversalJar,
);
/**
* Save the universal jar file name for later.
*
* We will need it to process launchscripts.
*/
g_forgeJar = upath.basename(forgeUniversalPath);
/**
* Finally, fetch libraries.
*/
const libraries = forgeProfile.libraries.filter((x) =>
Boolean(x?.downloads?.artifact?.url),
);
logInfo(`Fetching ${libraries.length} server libraries...`);
await Promise.all(
libraries.map(async (library) => {
const libraryPath = library.downloads.artifact.path;
const def: FileDef = {
url: library.downloads.artifact.url,
};
if (library.downloads.artifact.sha1) {
def.hashes = [
{ id: "sha1", hashes: [library.downloads.artifact.sha1] },
];
}
const destPath = upath.join(
serverDestDirectory,
"libraries",
libraryPath,
);
await fs.promises.mkdir(upath.dirname(destPath), { recursive: true });
return fs.promises.copyFile(
(await downloadOrRetrieveFileDef(def)).cachePath,
destPath,
);
}),
);
}
/**
* Download the server jar.
*/
async function downloadMinecraftServer() {
logInfo("Fetching the Minecraft version manifest...");
const versionManifest = await getVersionManifest(
modpackManifest.minecraft.version,
);
if (!versionManifest) {
throw new Error(`No manifest found for Minecraft ${versionManifest}`);
}
/**
* Fetch the server jar file.
*
* Pass SHA1 hash to compare against the downloaded file.
*/
const serverJar = await downloadOrRetrieveFileDef({
url: versionManifest.downloads.server.url,
hashes: [{ id: "sha1", hashes: versionManifest.downloads.server.sha1 }],
});
if (!(versionManifest.downloads && versionManifest.downloads.server)) {
throw new Error(`No server jar file found for ${versionManifest.id}`);
}
const dest = upath.join(
serverDestDirectory,
`minecraft_server.${versionManifest.id}.jar`,
);
await fs.promises.copyFile(upath.resolve(serverJar.cachePath), dest);
}
/**
* Copies server & shared mods.
*/
async function copyServerMods() {
return src(["*", upath.join("server", "*")], {
cwd: modDestDirectory,
resolveSymlinks: true,
encoding: false,
}).pipe(dest(upath.join(serverDestDirectory, "mods")));
}
/**
* Copies modpack overrides.
*/
function copyServerOverrides() {
return src(buildConfig.copyFromSharedServerGlobs, {
cwd: sharedDestDirectory,
allowEmpty: true,
resolveSymlinks: true,
encoding: false,
}).pipe(dest(upath.join(serverDestDirectory)));
}
/**
* Copies files from ./serverfiles into dest folder.
*/
function copyServerFiles() {
return src(["../serverfiles/**"], {
encoding: false, // Needed because of the Server Icon
}).pipe(dest(serverDestDirectory));
}
/**
* Copies the license file.
*/
function copyServerLicense() {
return src("../LICENSE").pipe(dest(serverDestDirectory));
}
/**
* Copies the update notes file.
*/
function copyServerUpdateNotes() {
return src("../UPDATENOTES.md", { allowEmpty: true }).pipe(
dest(serverDestDirectory),
);
}
/**
* Copies the changelog file.
*/
function copyServerChangelog() {
return src(
upath.join(buildConfig.buildDestinationDirectory, "CHANGELOG.md"),
).pipe(dest(serverDestDirectory));
}
/**
* Copies files from ./launchscripts into dest folder and processes them using mustache.
*
* Replaces jvmArgs, minRAM, maxRAM and forgeJar.
*/
function processLaunchscripts() {
const rules = {
jvmArgs: buildConfig.launchscriptsJVMArgs,
minRAM: buildConfig.launchscriptsMinRAM,
maxRAM: buildConfig.launchscriptsMaxRAM,
forgeJar: "",
};
if (g_forgeJar) {
rules.forgeJar = g_forgeJar;
} else {
logWarn("No forgeJar specified!");
logWarn("Did downloadForge task fail?");
}
return src(["../launchscripts/**"])
.pipe(
through.obj((file, _, callback) => {
if (file.isBuffer()) {
const rendered = mustache.render(file.contents.toString(), rules);
file.contents = Buffer.from(rendered);
}
callback(null, file);
}),
)
.pipe(dest(serverDestDirectory));
}
export default series(
serverCleanUp,
createServerDirs,
downloadForge,
downloadMinecraftServer,
copyServerMods,
copyServerOverrides,
copyServerFiles,
copyServerLicense,
copyServerChangelog,
copyServerUpdateNotes,
processLaunchscripts,
);