import {dest, series, src} from 'gulp'; import gulp from 'gulp'; import process from 'node:process'; import replace from 'gulp-replace'; import jsonModify from 'gulp-json-modify'; import {subtle} from 'node:crypto'; import * as dartSass from 'sass'; import gulpSass from 'gulp-sass'; import {deleteAsync} from 'del'; import {readdirSync, readFileSync, writeFileSync, rmdirSync, existsSync, mkdirSync, statSync} from "fs"; import {join} from 'node:path'; import {compilePack} from '@foundryvtt/foundryvtt-cli'; const sass = gulpSass(dartSass) /** * Generate a random alphanumeric string ID of a given requested length using `crypto.getRandomValues()`. * @param {string} reference The reference which should be used to generate a semi random ID * @param {number} length The length of the random string to generate, which must be at most 16384. * @returns {string} A string containing random letters (A-Z, a-z) and numbers (0-9). */ function randomID(reference = "", length = 16) { const encoder = new TextEncoder() const data = encoder.encode(reference) return subtle.digest('SHA-256', data).then(hashBuffer => { // Step 2: Convert the hash to a Base62 string const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; const hashArray = new Uint8Array(hashBuffer) let num = BigInt(0) // Convert hash buffer to a BigInt for (let byte of hashArray) { num = (num << BigInt(8)) | BigInt(byte) } let base62Id = ''; while (num > 0) { const remainder = num % BigInt(62) base62Id = base62Chars[Number(remainder)] + base62Id num = num / BigInt(62) } // Step 3: Return the first 16 characters return base62Id.slice(-length) }); } const convert = function (from, to, ofType, overwrite = true) { const SOURCE = from; const DEST = to; const TYPE = ofType; if (overwrite) { try { rmdirSync(DEST, {force: true, recursive: true}) } catch (e) { } mkdirSync(DEST) } let promises = [] const filewalker = async (source) => { console.debug("entering directory", source) for (let file of readdirSync(source)) { if (statSync(join(source, file)).isDirectory()) { await filewalker(join(source, file)) } else { if (file.endsWith(".json")) { console.debug("processing file", join(source, file)) let originalSource = JSON.parse(readFileSync(join(source, file), {encoding: "utf8"})) promises.push(new Promise((resolve2) => { randomID("DSA_4-1" + TYPE + originalSource.name.trim()).then(id => { let targetSource = { _id: id, _key: "!items!" + id, type: TYPE, img: originalSource.image, name: originalSource.name.trim(), system: {...originalSource}, } delete targetSource.system.image; let target = JSON.stringify(targetSource, null, 2) let newFileName = "./" + join(DEST, id + ".json") if (!existsSync(join("./", DEST))) { mkdirSync(join("./", DEST)) } writeFileSync(newFileName, target, {encoding: "utf8"}) resolve2() }) })) } } } } filewalker(SOURCE) return Promise.allSettled(promises) } function cleanDist() { return deleteAsync(['dist/**']) } function buildStyles() { return src('src/style/**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(dest('dist/style/')) } function copySource() { return src(['src/**/*', '!src/assets/**/*', '!src/style/**/*.scss', '!src/packs/**/*']) .pipe(dest('dist/')); } function copyAssets() { return src(['src/assets/**/*'], {encoding: false}) .pipe(dest('dist/assets/')) } function updateManifestFile() { return src('src/system.json') .pipe( jsonModify({ key: "version", value: process.env.VERSION }) ) .pipe( jsonModify({ key: "download", value: "https://git.macniel.online/macniel/foundry-dsa41-game/releases/download/{{VERSION}}/release.zip".replace("{{VERSION}}", process.env.VERSION) }) ) .pipe(dest('src/')) } gulp.task('prepareDB', async function (done) { try { if (!existsSync("./src/packs/__source")) { mkdirSync("./src/packs/__source"); } await convert("./src/packs/_source/talente", "./src/packs/__source/talente", "Skill") await convert("./src/packs/_source/zauber", "./src/packs/__source/zauber", "Spell") await convert("./src/packs/_source/vorteile", "./src/packs/__source/vorteile", "Advantage") await convert("./src/packs/_source/nachteile", "./src/packs/__source/vorteile", "Advantage", false) await convert("./src/packs/_source/sonderfertigkeiten", "./src/packs/__source/sonderfertigkeiten", "SpecialAbility") await convert("./src/packs/_source/waehrungen", "./src/packs/__source/waehrungen", "Equipment") await convert("./src/packs/_source/Gegenstaende/Waffen", "./src/packs/__source/waffen", "Equipment") await convert("./src/packs/_source/Gegenstaende/Munition", "./src/packs/__source/munition", "Equipment") await convert("./src/packs/_source/Gegenstaende/Ruestzeug", "./src/packs/__source/ruestzeug", "Equipment") await convert("./src/packs/_source/Gegenstaende/Behaelter", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Bekleidung", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Beleuchtung", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Buecher", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Essutensilien", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Sonstiges", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Werkzeug", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/Gegenstaende/Seile", "./src/packs/__source/gegenstaende", "Equipment", false) await convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy") await convert("./src/packs/_source/wunden", "./src/packs/__source/wunden", "ActiveEffect") await convert("./src/packs/_source/kulturen", "./src/packs/__source/kulturen", "Culture") await convert("./src/packs/_source/spezien", "./src/packs/__source/spezien", "Species") await convert("./src/packs/_source/professionen", "./src/packs/__source/professionen", "Profession") done() } catch (err) { console.error(err) } }) gulp.task('buildDB', function (done) { // Determine which source folders to process const PACK_SRC = "src/packs/__source" const PACK_DEST = "dist/packs/" return new Promise(async (resolve, reject) => { const folders = readdirSync(PACK_SRC, {withFileTypes: true}).filter(file => file.isDirectory() ); for (const folder of folders) { const src = join(PACK_SRC, folder.name) const dest = join(PACK_DEST, folder.name) console.info(`Compiling pack ${folder.name}`) await compilePack(src, dest, {recursive: true, nedb: false}) } resolve() done() }) }) export default series( cleanDist, updateManifestFile, copySource, copyAssets, buildStyles, gulp.task('prepareDB'), gulp.task('buildDB') )