Skip to content

Commit

Permalink
Source code deleting/copying
Browse files Browse the repository at this point in the history
Account for source code and item previews specified in all .cfg files during building, watching and publishing
Add --source flag to copy src to <bundleDir>/source
cfg module takes file paths instead of mod names
cfg files always have .cfg extension when specified via --cfg
info --cfg -> info --show-cfg to distinguish from overall --cfg flag
Delete created cfg file if failed to publish
Allow specifying content field in .cfg file when creating/publishing
  • Loading branch information
unshame committed Jul 21, 2018
1 parent 837e8a6 commit aba2568
Show file tree
Hide file tree
Showing 14 changed files with 194 additions and 100 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Made in [Node.js](https://nodejs.org/en/). Compiled with [pkg](https://github.co
### Prerequisites

1. Vermintide Mod SDK must be installed. Look for *"Warhammer: End Times - Vermintide Mod SDK Pre-Alpha"* for Vermintide 1 and *"Warhammer: Vermintide 2 SDK (Alpha)"* for Vermintide 2 in the Tools section in your Steam library.
5. [V2 ONLY] Switch Vermintide 2 Mod SDK to the *latest* branch in Properties > Betas.
4. [V1 ONLY] For now, to enable mods in the launcher, find `launcher.config` in `%AppData%\Fatshark\Warhammer End Times Vermintide` and set `ModsEnabled` to `true`, or add `ModsEnabled = true` if it is missing.
3. Steam must be running for creating, publishing and uploading mods.
4. Subscribe to Vermintide Mod Framework on Steam workshop ([V1 version](https://steamcommunity.com/sharedfiles/filedetails/?id=1289946781), [V2 version](https://steamcommunity.com/sharedfiles/filedetails/?id=1369573612)) and make sure that it is the first mod in the list in the launcher if you want VMF-dependent mods to work.
Expand All @@ -16,7 +15,7 @@ Made in [Node.js](https://nodejs.org/en/). Compiled with [pkg](https://github.co
1. Download and export **[the latest release](https://github.com/Vermintide-Mod-Framework/Vermintide-Mod-Builder/releases)**.
2. Run vmb.exe to create default .vmbrc config file in the folder with the executable.
3. Set `game` in .vmbrc to 1 or 2 to determine for which game mods are going to be created, built and uploaded by default.
4. Run `vmb create <mod_name>` to create a new mod. This will create a new VMF-dependent mod in the `mods` folder from a template and then open a steam workshop page where you will have to subscribe to the mod in order for the game to recognize it. Note that the mod you're subscribing to is not functional at this stage and will prevent you from entering the game until you build it properly.
4. Run `vmb create <mod_name>` to create a new mod. This will create a new VMF-dependent mod in the `mods` folder from a template and then open a steam workshop page where you will have to subscribe to the mod in order for the game to recognize it.
5. The main functionality of your mod should be added to `<mod_name>/scripts/mods/<mod_name>/<mod_name>.lua`.
6. To build the mod, run `vmb build <mod_name>`.
7. To upload an updated version of your mod, run `vmb upload <mod_name>`.
Expand All @@ -41,15 +40,15 @@ Run without command to see version number and a list of commands with parameters

#### Create a mod from template:

vmb create <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}] [--tags "<tag1>; <tag2>;..."] [--template <template_folder>]
vmb create <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}] [-c <content_folder>] [--tags "<tag1>; <tag2>;..."] [--template <template_folder>]

This will copy the template from specified template folder (either in .vmbrc or via the parameter) to a new folder, upload a placeholder mod to the workshop (the item is private by default), add its item ID to `itemV1.cfg` or `itemV2.cfg` (depending on which game is specified in the .vmbrc) in the new mod folder and open a browser window for you to subscribe to the mod.
This is needed for the game to recognize the mod.
By default, the template is for VMF-dependent mods. To create a VMF-independent mod specify `.template` as the template. See [Mod Templates](#mod-templates).

#### Publish an existing mod to Steam Workshop:

vmb publish <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}] [--tags "<tag1>; <tag2>;..."] [--ignore-errors] [--verbose] [--clean]
vmb publish <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}] [-c <content_folder>] [--tags "<tag1>; <tag2>;..."] [--ignore-errors] [--verbose] [--clean] [--source]

This will create `itemV1.cfg` or `itemV2.cfg` for a mod if it doesn't exist then build and publish the mod to workshop as a new item.
If .cfg file is present it shouldn't have `published_id` in it.
Expand All @@ -72,11 +71,11 @@ I can't be bothered to add parameters to change the title, description etc. You

#### Build all or specified mods from current directory:

vmb build [<mod_name1> <mod_name2>...] [--ignore-errors] [--verbose] [--clean] [--id <item_id>] [--no-workshop]
vmb build [<mod_name1> <mod_name2>...] [--ignore-errors] [--verbose] [--clean] [--id <item_id>] [--no-workshop] [--source]

#### Automatically build all or specified mods from current directory on changes:

vmb watch [<mod_name1> <mod_name2>...] [--ignore-errors] [--verbose] [--clean] [--id <item_id>] [--no-workshop]
vmb watch [<mod_name1> <mod_name2>...] [--ignore-errors] [--verbose] [--clean] [--id <item_id>] [--no-workshop] [--source]

Two of the commands above will build and copy the bundle to the bundleV1 or bundleV2 folder, as well as replace the old bundle in Steam Workshop folder with the new one. If no mod name is specified, all mods will be built/watched.
`itemV1.cfg` or `itemV2.cfg` needs to be in the folder with mod's source code and have `published_id` line.
Expand All @@ -86,6 +85,7 @@ You can also enable this parameter by default by setting `ignore_build_errors` i
`--clean` - deletes the temp folder instead of overwriting it (builds slower, use to force building from scratch).
`--id` - forces item ID. This way you can build a mod without having a .cfg file in its folder. Can only be passed if building one mod.
`--no-workshop` - this will build the mod even if .cfg file isn't present but will only copy it to the bundle folder in mod's folder.
`--source` - this will copy the source code of the mod into `<bundle_folder>/source`. This includes all files inside the mod folder, except .cfg and item preview files, and excluding bundle folders.

#### Quickly change configuration in .vmbrc:

Expand All @@ -96,10 +96,10 @@ Note that you can only set string, number and boolean-type options this way.

#### Show information about all or some mods:

vmb info [<mod_name1> <mod_name2>...] [--cfg]
vmb info [<mod_name1> <mod_name2>...] [--show-cfg]

This will show the full path to the mod's folder, whether the mod has been published, when it was last built and whether `itemVX.cfg` file is present.
`--cfg` will also print the contents of the `itemVX.cfg` file.
`--show-cfg` will also print the contents of the `itemVX.cfg` file.

### Configuration

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vmb",
"version": "1.4.0",
"version": "1.5.0",
"description": "Vermintide Mod Builder",
"main": "vmb.js",
"bin": "vmb.js",
Expand Down
36 changes: 25 additions & 11 deletions scripts/modules/cfg.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ module.exports = function cfg() {
module.exports.getPath = getPath;
module.exports.getDir = getDir;
module.exports.writeFile = writeFile;
module.exports.fileExists = fileExists;
module.exports.readFile = readFile;
module.exports.getValue = getValue;
module.exports.getMappedValue = getMappedValue;

init();

Expand All @@ -28,6 +28,12 @@ const modTools = require('../tools/mod_tools');
let base = '';
let relativeDir = '';

// Name of key in vmb source code - name and type of key in .cfg files
let mappedKeys = {
bundleDir: { key: 'content', type: 'string' },
itemPreview: { key: 'preview', type: 'string' }
};

// Sets up paths based on cl and config
function init() {

Expand All @@ -36,7 +42,7 @@ function init() {

// Set paths to custom cfg path, or use default one
if (cfgArg) {
let cfgPath = path.parse(String(cfgArg));
let cfgPath = path.parse(String(cfgArg) + '.cfg');
setBase(cfgPath.base);
setRelativeDir(cfgPath.dir);
}
Expand Down Expand Up @@ -94,25 +100,20 @@ async function writeFile(params) {
let configText = `title = "${params.title}";\n` +
`description = "${params.description}";\n` +
`preview = "${config.get('itemPreview')}";\n` +
`content = "${config.get('defaultBundleDir')}";\n` +
`content = "${params.content || config.get('defaultBundleDir')}";\n` +
`language = "${params.language}";\n` +
`visibility = "${params.visibility}";\n` +
`tags = [${tags}]`;

console.log(`${base}:`);
console.log(` ${str.rmn(configText).replace(/\n/g, '\n ')}`);

return await pfs.writeFile(getPath(params.name), configText);
}

// Check if .cfg file exists
async function fileExists(modName) {
return await pfs.accessible(getPath(modName));
return await pfs.writeFile(params.filePath, configText);
}

// Returns .cfg file's data
async function readFile(modName) {
return await pfs.readFile(getPath(modName), 'utf8');
async function readFile(filePath) {
return await pfs.readFile(filePath, 'utf8');
}

// Gets key value from .cfg file data
Expand All @@ -138,3 +139,16 @@ function getValue(data, key, type) {

return match && Array.isArray(match) && match.length > 1 ? match[1] : null;
}

// Gets value of a keys from mappedKeys array, throws if value isn't found
function getMappedValue(filePath, data, mappedKey) {

let { key, type } = mappedKeys[mappedKey];
let value = getValue(data, key, type);

if (typeof value != type) {
throw new Error(`No '${key}' value specified` + (filePath ? ` in "${filePath}"` : ''));
}

return value;
}
2 changes: 1 addition & 1 deletion scripts/modules/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ function _getGameNumber(gameNumber) {
return gameNumber;
}

// Gets absolute template path from cl/config data
// Gets absolute template path from cl/config data
function _getTemplateDir(templateDir) {
let newTemplateDir = cl.get('template') || '';

Expand Down
5 changes: 3 additions & 2 deletions scripts/tasks/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = async function taskBuild() {

let exitCode = 0;

let { modNames, verbose, shouldRemoveTemp, modId, makeWorkshopCopy, ignoreBuildErrors } = await modTools.getBuildParams();
let { modNames, verbose, shouldRemoveTemp, modId, makeWorkshopCopy, ignoreBuildErrors, copySource } = await modTools.getBuildParams();

// Only print what we're gonna build if there's more than one mod
if (modNames.length > 1) {
Expand Down Expand Up @@ -46,7 +46,8 @@ module.exports = async function taskBuild() {
makeWorkshopCopy,
verbose,
ignoreBuildErrors,
modId
modId,
copySource
});
}
catch (error) {
Expand Down
5 changes: 3 additions & 2 deletions scripts/tasks/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ module.exports = async function taskCreate() {

// Copy placeholder bundle or .mod file depending on format used
if (config.get('useNewFormat')) {
await templater.createPlaceholderModFile(modName);
await templater.createPlaceholderModFile(modName, params.content);
}
else {
await templater.createPlaceholderBundle(modName);
await templater.createPlaceholderBundle(modName, params.content);
}

// Create .cfg file
params.filePath = cfg.getPath(modName);
await cfg.writeFile(params);

// Get path tosdk and upload mod
Expand Down
11 changes: 6 additions & 5 deletions scripts/tasks/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ module.exports = function taskHelp() {
'vmb config [--<key1>=<value1> --<key2>=<value2>...]\n\n' +

'vmb create <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}]\n' +
' [--tags "<tag1>; <tag2>;..."] [--template <template_folder>]\n\n' +
' [-c <content_folder>] [--tags "<tag1>; <tag2>;..."] [--template <template_folder>]\n\n' +

'vmb publish <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}]\n' +
' [--tags "<tag1>; <tag2>;..."] [--ignore-errors] [--verbose] [--clean]\n\n' +
' [-c <content_folder>] [--tags "<tag1>; <tag2>;..."]\n' +
' [--ignore-errors] [--verbose] [--clean] [--source]\n\n' +

'vmb upload {<mod_name1> <mod_name2>... | --all} [-n <changenote>] [--open] [--skip]\n\n' +

'vmb open {<mod_name> | --id <item_id>}\n\n' +

'vmb build [<mod_name1> <mod_name2>...] [--ignore-errors] [--verbose] [--clean] [--id <item_id>]\n' +
' [--no-workshop]\n\n' +
' [--no-workshop] [--source]\n\n' +

'vmb watch [<mod_name1> <mod_name2>...] [--ignore-errors] [--verbose] [--clean] [--id <item_id>]\n' +
' [--no-workshop]\n\n' +
' [--no-workshop] [--source]\n\n' +

'vmb info [<mod_name1> <mod_name2>...] [--cfg]\n\n' +
'vmb info [<mod_name1> <mod_name2>...] [--show-cfg]\n\n' +
'See README.md for more information.'
);
return { exitCode: 0, finished: false };
Expand Down
4 changes: 2 additions & 2 deletions scripts/tasks/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = async function taskInfo() {
let exitCode = 0;

let modNames = await modTools.getModNames();
let showCfg = cl.get('cfg') || false;
let showCfg = cl.get('show-cfg') || false;

if (modNames.length > 1) {
console.log(`Showing information for mods:`);
Expand Down Expand Up @@ -110,7 +110,7 @@ module.exports = async function taskInfo() {

if (showCfg && cfgExists) {
console.log(`${cfgBase} in "${cfgDir}":`);
let cfgData = await cfg.readFile(modName);
let cfgData = await cfg.readFile(cfg.getPath(modName));
cfgData = str.rmn(cfgData).replace(/^/gm, ' ');
console.log(cfgData);
}
Expand Down
25 changes: 21 additions & 4 deletions scripts/tasks/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ module.exports = async function taskPublish() {
makeWorkshopCopy: false,
verbose: buildParams.verbose,
ignoreBuildErrors: buildParams.ignoreBuildErrors,
modId: null
modId: null,
copySource: buildParams.copySource
});

console.log();
Expand All @@ -69,6 +70,15 @@ module.exports = async function taskPublish() {
await opn(modUrl);
}
catch (error) {

// Clean up .cfg file if it was created
if (params.cfgCreated) {
try {
await pfs.unlink(cfg.getPath(modName));
}
catch (err) {}
}

print.error(error);
exitCode = 1;
}
Expand Down Expand Up @@ -99,24 +109,31 @@ async function _getPublishParams() {
}

// Read .cfg file if it exists, or create it based on params
let cfgPath = cfg.getPath(modName);
let cfgData = '';
params.cfgCreated = false;
try {
cfgData = await cfg.readFile(modName);
cfgData = await cfg.readFile(cfgPath);
}
catch (error) {
params.filePath = cfgPath;
await cfg.writeFile(params);
params.cfgCreated = true;
}

if (cfgData) {

// Take image preview file name from .cfg
params.itemPreview = cfg.getValue(cfgData, 'preview', 'string') || params.itemPreview;
try {
params.itemPreview = cfg.getMappedValue(cfgPath, cfgData, 'itemPreview');
}
catch (error) {}

// Check if mod has been published already
if (cfg.getValue(cfgData, 'published_id', 'number')) {

throw new Error(
`Mod has already been published with item cfg "${cfg.getPath(modName)}".\n` +
`Mod has already been published with item cfg "${cfgPath}".\n` +
`Use 'vmb upload' or specify a different item cfg file with --cfg instead.`
);
}
Expand Down
17 changes: 12 additions & 5 deletions scripts/tasks/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = async function taskWatch() {

let exitCode = 0;

let { modNames, verbose, shouldRemoveTemp, modId, makeWorkshopCopy, ignoreBuildErrors } = await modTools.getBuildParams();
let { modNames, verbose, shouldRemoveTemp, modId, makeWorkshopCopy, ignoreBuildErrors, copySource } = await modTools.getBuildParams();

if (modNames.length === 0) {
console.log(`No mods to watch`);
Expand Down Expand Up @@ -47,15 +47,21 @@ module.exports = async function taskWatch() {
bundleDir = modTools.getDefaultBundleDir(modName);
}

let { bundleDirs } = await builder.getRelevantCfgParams(modName, bundleDir);

// These files will be watched
let src = [
modDir,

// Ignore temp files stingray creates and folder with built files
'!' + modDir + '/*.tmp',
'!' + bundleDir + '/*'
// Ignore temp files stingray creates
'!' + modDir + '/*.tmp'
];

// Ignore folders with built files
for (let bundleDir of bundleDirs) {
src.push('!' + bundleDir + '/**');
}

watch(src, async (callback) => {

try {
Expand All @@ -64,7 +70,8 @@ module.exports = async function taskWatch() {
makeWorkshopCopy,
verbose,
ignoreBuildErrors,
modId
modId,
copySource
});
}
catch (error) {
Expand Down
Loading

0 comments on commit aba2568

Please sign in to comment.