Packaging test
Example apps / packaging-test / v0.2.1
Node modbridge permissionneeds codewareneeds archivexlneeds redbridgeneeds not-a-real-mod
A manifest that requires other mods (Codeware, ArchiveXL, REDbridge and one that does not exist), and REDbridge reading the game's archives.
The preview's REDbridge knows a few made-up files.
// Packaging test: a sample of W2 and W3. Its rednode-app.json requires Codeware, ArchiveXL,
// REDbridge and a mod that does not exist, so the Mod Manager and this window show one missing.
// kernel.bridge() reaches REDbridge (WolvenKit's engine) to list and search the game's
// archives. Needs "permissions": ["bridge"] and the player's OK in the Mod Manager.
import { kernel, ui } from 'rednode';
let query = 'player_ma_tpp';
// Met requirements in the normal colour, missing ones in red: one red line used to turn them all red.
const requires = ui.text('Checking requirements...', { tone: 'dim' });
const missing = ui.text('', { tone: 'danger' });
const bridgeLine = ui.text('Press Archives to start REDbridge.', { tone: 'dim' });
const results = ui.text('', { tone: 'dim' });
ui.window({
title: 'Packaging test',
anchor: 'left',
x: 40,
y: 120,
width: 440,
height: 460,
children: [
requires,
missing,
ui.row(ui.button('Archives', listArchives), ui.button('Search', search, { variant: 'ghost' })),
ui.input({
placeholder: 'Search the archives (Enter)',
value: query,
onChange: (v) => (query = v),
onSubmit: (v) => {
query = v;
search();
},
}),
bridgeLine,
results,
],
});
// What rednode-app.json requires, checked against the game folder.
const checked = await kernel.requirements();
const line = (r) => `${r.ok ? 'OK' : 'MISSING'} ${r.name} ${r.wants} ${r.found ?? r.note}`;
requires.set({ text: checked.filter((r) => r.ok).map(line).join('\n'), tone: 'normal' });
missing.set({ text: checked.filter((r) => !r.ok).map(line).join('\n') });
console.log(`Requirements: ${checked.map((r) => `${r.name} ${r.ok ? 'ok' : r.note}`).join(', ')}`);
async function bridge() {
const b = await kernel.bridge();
bridgeLine.set({ text: `REDbridge ${b.version ?? ''} from ${b.source}`, tone: 'dim' });
return b;
}
async function listArchives() {
try {
const b = await bridge();
const t0 = Date.now();
const info = await b.call('bridge.info');
const archives = await b.call('archive.archives');
results.set({
text: `${archives.length} archives, ${info.files} files (${Date.now() - t0} ms)\n` +
archives.slice(0, 6).map((a) => `${a.name} ${a.files}`).join('\n'),
tone: 'normal',
});
console.log(`REDbridge: ${archives.length} archives, ${info.files} files`);
} catch (err) {
results.set({ text: err.message, tone: 'danger' });
console.log(`REDbridge failed: ${err.message}`);
}
}
async function search() {
if (!query.trim()) return;
try {
const b = await bridge();
const found = await b.call('archive.search', { query, limit: 8 });
results.set({ text: `${found.total} found\n${found.items.map((f) => f.name).join('\n')}`, tone: 'normal' });
console.log(`Searched "${query}": ${found.total} found`);
} catch (err) {
results.set({ text: err.message, tone: 'danger' });
console.log(`Search failed: ${err.message}`);
}
}
REDnode runs JavaScript. The rednode package also has TypeScript types, so an editor checks these calls; compile TypeScript to .mjs first.
{
"id": "packaging-test",
"name": "Packaging test",
"version": "0.2.1",
"author": "Edgemodders",
"description": "A sample of W2 and W3: \"requires\" in rednode-app.json (Codeware, ArchiveXL, REDbridge, and one mod that does not exist, so the Mod Manager shows it missing) and kernel.bridge(), which lists and searches the game's archives through REDbridge. Asks for the bridge permission.",
"main": "mod.mjs",
"permissions": ["bridge"],
"requires": {
"codeware": ">=1.16",
"archivexl": ">=1.23",
"redbridge": ">=0.10",
"not-a-real-mod": "*"
}
}
The manifest names the package and says how REDnode runs it. Its fields are in Overlay apps.
How it works
requires lists other mods with a version. The Mod Manager checks them against the game folder and shows what is missing.
REDbridge is WolvenKit's engine. kernel.bridge() starts it once and shares it, so a package can list and search the game's archives.
What it uses
requires kernel.requirements kernel.bridge "bridge" permission
Use it
Copy the files into red4ext/plugins/REDnode/apps/packaging-test/ in the game folder, or start your own package from them: change id and name in the manifest first. The whole folder is on GitHub.