factorio-recipes/index.js

109 lines
4.5 KiB
JavaScript

const { readdirSync: readDir, readFileSync } = require("fs");
const { readFile, writeFile, mkdir } = require("fs/promises");
const homeDir = require("os").homedir()
const { join: joinPath, relative: relativePath, sep: pathSeparator } = require("path");
const { LuaFactory } = require('wasmoon')
const FACTORIO_DATA_DIR = process.env["FACTORIO_DATA_DIR"] ?? `${homeDir}/.steam/steam/steamapps/common/Factorio/data`;
const LOCALE = process.env["LOCALE"] ?? "en";
const MODS = "core;base" + (process.env["MODS"] ??
((process.env["SPACE_AGE"] ?? "true") == "true" ? ";quality;elevated-rails;space-age" : ""));
const mods = MODS.split(";").filter(v => v);
const readLocaleData = mod => readFileSync(joinPath(FACTORIO_DATA_DIR, mod, "locale", LOCALE, mod + ".cfg"), "utf-8")
.trim()
.split("\n")
.filter(v => v)
.map(v => v.split(/=(.*)/, 2))
.flatMap((category = null, ([ k, v ]) => k.startsWith("[") ? (category = k, []) : { key: k, value: v, category }))
.reduce((m, { key, value, category }) => ((m.get(category) ?? m.set(category, new Map()).get(category)).set(key, value), m), new Map());
const defines = [...readFileSync(joinPath(FACTORIO_DATA_DIR, "..", "doc-html", "defines.html"), "utf8")
.matchAll(/id="(defines\..*?)"/g)]
.map(v => v[1])
.flatMap((v, i) => i === 0 ? ["defines", v] : v) // prepend 'defines'
.map(v => [...v.matchAll(/([a-zA-Z0-9_]+)|\['(.+?)'\]/g)].map(([ , a, b ]) => a ?? b))
.reduce((acc, keys, idx) => {
let v = acc;
let lastKey = keys.pop();
keys.forEach(k => (typeof v[k] != "object" ? v[k] = {} : null, v = v[k]));
v[lastKey] = idx;
return acc;
}, {})
.defines;
const loadModFactory = async () => {
const luaFactory = new LuaFactory();
const lua = await luaFactory.createEngine();
await luaFactory.mountFile("serpent.lua", await readFile(joinPath("serpent", "src", "serpent.lua")));
await lua.doString("serpent = require('serpent')");
await Promise.all(readDir(joinPath(FACTORIO_DATA_DIR), { recursive: true, withFileTypes: true })
.filter(v => v.isFile() && v.name.endsWith(".lua"))
.map(async file => {
const data = await readFile(joinPath(file.parentPath, file.name));
if (file.parentPath.endsWith("lualib")) {
await luaFactory.mountFile(file.name, data);
}
const rPath = relativePath(joinPath(FACTORIO_DATA_DIR), joinPath(file.parentPath, file.name))
.split(pathSeparator);
const luaModule = rPath.shift();
// console.log(joinPath(`__${luaModule}__`, ...rPath))
await luaFactory.mountFile(joinPath(`__${luaModule}__`, ...rPath), data);
}));
lua.global.set("defines", defines);
await lua.doString("math.atan2 = function(x, y) return math.atan(y / x) end");
await lua.doString("require('dataloader')");
return async mod => {
const files = readDir(joinPath(FACTORIO_DATA_DIR, mod), { recursive: true, withFileTypes: true });
await lua.doString(files
.filter(v => v.isFile() && v.name.endsWith(".lua") && !v.parentPath.endsWith("lualib") && v.parentPath.indexOf("prototype") >= 0)
.map(file => relativePath(joinPath(FACTORIO_DATA_DIR, mod), joinPath(file.parentPath, file.name)).replace(/\.lua$/, ""))
.flatMap(file => [ file, file.replaceAll(pathSeparator, '.') ])
.reduce((s, p) => s + `package.loaded['${p}'] = nil\n`, ""));
await Promise.all(files
.filter(v => v.isFile() && v.name.endsWith(".lua") && !v.parentPath.endsWith("lualib"))
.map(async file => {
const moduleName = relativePath(joinPath(FACTORIO_DATA_DIR, mod), joinPath(file.parentPath, file.name))
return luaFactory.mountFile(moduleName,
(mod === "space-age" && moduleName === "prototypes/ambient-sounds.lua" ? "require('__space-age__/sound/ambient/space/interlude-6/interlude-6')" : "") + // no clue why this fix is needed
await readFile(joinPath(file.parentPath, file.name)),
)
}));
console.log(`processing data, ${mod}`);
await lua.doFile("data.lua");
// enabling these results in a weird failure
// if (files.some(v => v.name === "data-updates.lua")) {
// console.log("\t data updates");
// await lua.doFile("data-updates.lua");
// }
//
// if (files.some(v => v.name === "data-final-fixes.lua")) {
// console.log("\t data final fixes");
// await lua.doFile("data-final-fixes.lua");
// }
return lua.global.get("data");
}
}
console.log("loading mods:", mods);
loadModFactory().then(async loadMod => {
let data = null;
for (const mod of mods) {
data = await loadMod(mod);
}
await mkdir("build", { recursive: true });
await writeFile(joinPath("build", "out.json"), JSON.stringify(data.raw));
});