Skip to content

Commit aba2568

Browse files
committed
Source code deleting/copying
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
1 parent 837e8a6 commit aba2568

File tree

14 files changed

+194
-100
lines changed

14 files changed

+194
-100
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ Made in [Node.js](https://nodejs.org/en/). Compiled with [pkg](https://github.co
66
### Prerequisites
77

88
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.
9-
5. [V2 ONLY] Switch Vermintide 2 Mod SDK to the *latest* branch in Properties > Betas.
109
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.
1110
3. Steam must be running for creating, publishing and uploading mods.
1211
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.
@@ -16,7 +15,7 @@ Made in [Node.js](https://nodejs.org/en/). Compiled with [pkg](https://github.co
1615
1. Download and export **[the latest release](https://github.com/Vermintide-Mod-Framework/Vermintide-Mod-Builder/releases)**.
1716
2. Run vmb.exe to create default .vmbrc config file in the folder with the executable.
1817
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.
19-
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.
18+
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.
2019
5. The main functionality of your mod should be added to `<mod_name>/scripts/mods/<mod_name>/<mod_name>.lua`.
2120
6. To build the mod, run `vmb build <mod_name>`.
2221
7. To upload an updated version of your mod, run `vmb upload <mod_name>`.
@@ -41,15 +40,15 @@ Run without command to see version number and a list of commands with parameters
4140

4241
#### Create a mod from template:
4342

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

4645
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.
4746
This is needed for the game to recognize the mod.
4847
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).
4948

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

52-
vmb publish <mod_name> [-d <description>] [-t <title>] [-l <language>] [-v {private|public|friends}] [--tags "<tag1>; <tag2>;..."] [--ignore-errors] [--verbose] [--clean]
51+
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]
5352

5453
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.
5554
If .cfg file is present it shouldn't have `published_id` in it.
@@ -72,11 +71,11 @@ I can't be bothered to add parameters to change the title, description etc. You
7271

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

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

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

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

8180
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.
8281
`itemV1.cfg` or `itemV2.cfg` needs to be in the folder with mod's source code and have `published_id` line.
@@ -86,6 +85,7 @@ You can also enable this parameter by default by setting `ignore_build_errors` i
8685
`--clean` - deletes the temp folder instead of overwriting it (builds slower, use to force building from scratch).
8786
`--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.
8887
`--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.
88+
`--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.
8989

9090
#### Quickly change configuration in .vmbrc:
9191

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

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

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

101101
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.
102-
`--cfg` will also print the contents of the `itemVX.cfg` file.
102+
`--show-cfg` will also print the contents of the `itemVX.cfg` file.
103103

104104
### Configuration
105105

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vmb",
3-
"version": "1.4.0",
3+
"version": "1.5.0",
44
"description": "Vermintide Mod Builder",
55
"main": "vmb.js",
66
"bin": "vmb.js",

scripts/modules/cfg.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ module.exports = function cfg() {
77
module.exports.getPath = getPath;
88
module.exports.getDir = getDir;
99
module.exports.writeFile = writeFile;
10-
module.exports.fileExists = fileExists;
1110
module.exports.readFile = readFile;
1211
module.exports.getValue = getValue;
12+
module.exports.getMappedValue = getMappedValue;
1313

1414
init();
1515

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

31+
// Name of key in vmb source code - name and type of key in .cfg files
32+
let mappedKeys = {
33+
bundleDir: { key: 'content', type: 'string' },
34+
itemPreview: { key: 'preview', type: 'string' }
35+
};
36+
3137
// Sets up paths based on cl and config
3238
function init() {
3339

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

3743
// Set paths to custom cfg path, or use default one
3844
if (cfgArg) {
39-
let cfgPath = path.parse(String(cfgArg));
45+
let cfgPath = path.parse(String(cfgArg) + '.cfg');
4046
setBase(cfgPath.base);
4147
setRelativeDir(cfgPath.dir);
4248
}
@@ -94,25 +100,20 @@ async function writeFile(params) {
94100
let configText = `title = "${params.title}";\n` +
95101
`description = "${params.description}";\n` +
96102
`preview = "${config.get('itemPreview')}";\n` +
97-
`content = "${config.get('defaultBundleDir')}";\n` +
103+
`content = "${params.content || config.get('defaultBundleDir')}";\n` +
98104
`language = "${params.language}";\n` +
99105
`visibility = "${params.visibility}";\n` +
100106
`tags = [${tags}]`;
101107

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

105-
return await pfs.writeFile(getPath(params.name), configText);
106-
}
107-
108-
// Check if .cfg file exists
109-
async function fileExists(modName) {
110-
return await pfs.accessible(getPath(modName));
111+
return await pfs.writeFile(params.filePath, configText);
111112
}
112113

113114
// Returns .cfg file's data
114-
async function readFile(modName) {
115-
return await pfs.readFile(getPath(modName), 'utf8');
115+
async function readFile(filePath) {
116+
return await pfs.readFile(filePath, 'utf8');
116117
}
117118

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

139140
return match && Array.isArray(match) && match.length > 1 ? match[1] : null;
140141
}
142+
143+
// Gets value of a keys from mappedKeys array, throws if value isn't found
144+
function getMappedValue(filePath, data, mappedKey) {
145+
146+
let { key, type } = mappedKeys[mappedKey];
147+
let value = getValue(data, key, type);
148+
149+
if (typeof value != type) {
150+
throw new Error(`No '${key}' value specified` + (filePath ? ` in "${filePath}"` : ''));
151+
}
152+
153+
return value;
154+
}

scripts/modules/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ function _getGameNumber(gameNumber) {
389389
return gameNumber;
390390
}
391391

392-
// Gets absolute template path from cl/config data
392+
// Gets absolute template path from cl/config data
393393
function _getTemplateDir(templateDir) {
394394
let newTemplateDir = cl.get('template') || '';
395395

scripts/tasks/build.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports = async function taskBuild() {
77

88
let exitCode = 0;
99

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

1212
// Only print what we're gonna build if there's more than one mod
1313
if (modNames.length > 1) {
@@ -46,7 +46,8 @@ module.exports = async function taskBuild() {
4646
makeWorkshopCopy,
4747
verbose,
4848
ignoreBuildErrors,
49-
modId
49+
modId,
50+
copySource
5051
});
5152
}
5253
catch (error) {

scripts/tasks/create.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ module.exports = async function taskCreate() {
4141

4242
// Copy placeholder bundle or .mod file depending on format used
4343
if (config.get('useNewFormat')) {
44-
await templater.createPlaceholderModFile(modName);
44+
await templater.createPlaceholderModFile(modName, params.content);
4545
}
4646
else {
47-
await templater.createPlaceholderBundle(modName);
47+
await templater.createPlaceholderBundle(modName, params.content);
4848
}
4949

5050
// Create .cfg file
51+
params.filePath = cfg.getPath(modName);
5152
await cfg.writeFile(params);
5253

5354
// Get path tosdk and upload mod

scripts/tasks/help.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ module.exports = function taskHelp() {
1010
'vmb config [--<key1>=<value1> --<key2>=<value2>...]\n\n' +
1111

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

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

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

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

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

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

28-
'vmb info [<mod_name1> <mod_name2>...] [--cfg]\n\n' +
29+
'vmb info [<mod_name1> <mod_name2>...] [--show-cfg]\n\n' +
2930
'See README.md for more information.'
3031
);
3132
return { exitCode: 0, finished: false };

scripts/tasks/info.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = async function taskInfo() {
1616
let exitCode = 0;
1717

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

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

111111
if (showCfg && cfgExists) {
112112
console.log(`${cfgBase} in "${cfgDir}":`);
113-
let cfgData = await cfg.readFile(modName);
113+
let cfgData = await cfg.readFile(cfg.getPath(modName));
114114
cfgData = str.rmn(cfgData).replace(/^/gm, ' ');
115115
console.log(cfgData);
116116
}

scripts/tasks/publish.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ module.exports = async function taskPublish() {
4747
makeWorkshopCopy: false,
4848
verbose: buildParams.verbose,
4949
ignoreBuildErrors: buildParams.ignoreBuildErrors,
50-
modId: null
50+
modId: null,
51+
copySource: buildParams.copySource
5152
});
5253

5354
console.log();
@@ -69,6 +70,15 @@ module.exports = async function taskPublish() {
6970
await opn(modUrl);
7071
}
7172
catch (error) {
73+
74+
// Clean up .cfg file if it was created
75+
if (params.cfgCreated) {
76+
try {
77+
await pfs.unlink(cfg.getPath(modName));
78+
}
79+
catch (err) {}
80+
}
81+
7282
print.error(error);
7383
exitCode = 1;
7484
}
@@ -99,24 +109,31 @@ async function _getPublishParams() {
99109
}
100110

101111
// Read .cfg file if it exists, or create it based on params
112+
let cfgPath = cfg.getPath(modName);
102113
let cfgData = '';
114+
params.cfgCreated = false;
103115
try {
104-
cfgData = await cfg.readFile(modName);
116+
cfgData = await cfg.readFile(cfgPath);
105117
}
106118
catch (error) {
119+
params.filePath = cfgPath;
107120
await cfg.writeFile(params);
121+
params.cfgCreated = true;
108122
}
109123

110124
if (cfgData) {
111125

112126
// Take image preview file name from .cfg
113-
params.itemPreview = cfg.getValue(cfgData, 'preview', 'string') || params.itemPreview;
127+
try {
128+
params.itemPreview = cfg.getMappedValue(cfgPath, cfgData, 'itemPreview');
129+
}
130+
catch (error) {}
114131

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

118135
throw new Error(
119-
`Mod has already been published with item cfg "${cfg.getPath(modName)}".\n` +
136+
`Mod has already been published with item cfg "${cfgPath}".\n` +
120137
`Use 'vmb upload' or specify a different item cfg file with --cfg instead.`
121138
);
122139
}

scripts/tasks/watch.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = async function taskWatch() {
99

1010
let exitCode = 0;
1111

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

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

50+
let { bundleDirs } = await builder.getRelevantCfgParams(modName, bundleDir);
51+
5052
// These files will be watched
5153
let src = [
5254
modDir,
5355

54-
// Ignore temp files stingray creates and folder with built files
55-
'!' + modDir + '/*.tmp',
56-
'!' + bundleDir + '/*'
56+
// Ignore temp files stingray creates
57+
'!' + modDir + '/*.tmp'
5758
];
5859

60+
// Ignore folders with built files
61+
for (let bundleDir of bundleDirs) {
62+
src.push('!' + bundleDir + '/**');
63+
}
64+
5965
watch(src, async (callback) => {
6066

6167
try {
@@ -64,7 +70,8 @@ module.exports = async function taskWatch() {
6470
makeWorkshopCopy,
6571
verbose,
6672
ignoreBuildErrors,
67-
modId
73+
modId,
74+
copySource
6875
});
6976
}
7077
catch (error) {

0 commit comments

Comments
 (0)