Initial implementation of Gulp based mission builder

This commit is contained in:
Filip Maciejewski 2018-02-13 01:14:12 +01:00
parent f11b69463f
commit b6a18f94fa
10 changed files with 5417 additions and 0 deletions

84
_tools/README.md Normal file
View File

@ -0,0 +1,84 @@
# KP Liberation builder
## Requirements
nodejs version >=7.
## Usage
```bash
# Install dependencies
npm install
# Run mission build
npm run build
# Run task with local gulp via npx
npx gulp <task_name>
# With gulp-cli and gulp 4 installed globally
gulp <task_name>
```
| Task | Desc |
| ----------- | ---------------------------------------------- |
| clean | removes `build/` dir |
| build | assembles missionfolder and sets config values |
| pbo | packs missionfolders into PBOs |
| zip | creates release ZIPs |
| __default__ | runs _build_, _pbo_ and _zip_ |
Build files will be outputted to `build/` dir.
## Configuration
### presets.json
This file should contain an JSON __array__ of `Presets`, for every preset one mission file will be built.
Every `Preset` entry should have following structure:
```javascript
{
// Source folder with mission.sqm, relative to <missionsFolder>
// If mission.sqm is in root of <missionsFolder> should be set to empty string
"sourceFolder": "kp_liberation.Altis",
// Name and map is used to build output directory: <missionName>.<map>
// Name different than source allows to build multiple version of mission on same map
// Combination of <missionName> and <map> should be unique for every preset
"missionName": "kp_liberation",
"map": "Altis",
// Keys of <variables> object represent variables in <configFile>.
// These variables values will be set to corresponding value in <variables>
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 0,
"KP_liberation_preset_opfor": 0,
"KP_liberation_preset_resistance": 0,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
}
```
### gulpfile.ts
`paths` variable in _gulpfile_ holds filesystem paths required to build missions.
```typescript
/**
* Mission folders configuration
*/
const paths: FolderStructureInfo = {
// Folder with mission scripts
frameworkFolder: resolve('..', 'Missionframework'),
// Folder with base mission.sqm folders
missionsFolder: resolve('..', 'Missionbasefiles'),
// Output directory
workDir: resolve("./build")
};
```

119
_tools/_presets.json Normal file
View File

@ -0,0 +1,119 @@
[
{
"sourceFolder": "kp_liberation.Altis",
"missionName": "kp_liberation",
"map": "Altis",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 0,
"KP_liberation_preset_opfor": 0,
"KP_liberation_preset_resistance": 0,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.Chernarus",
"missionName": "kp_liberation",
"map": "Chernarus",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 6,
"KP_liberation_preset_opfor": 2,
"KP_liberation_preset_resistance": 2,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.Malden",
"missionName": "kp_liberation",
"map": "Malden",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 0,
"KP_liberation_preset_opfor": 0,
"KP_liberation_preset_resistance": 0,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.Takistan",
"missionName": "kp_liberation",
"map": "Takistan",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 7,
"KP_liberation_preset_opfor": 3,
"KP_liberation_preset_resistance": 3,
"KP_liberation_preset_civilians": 2,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.Takistan",
"missionName": "kp_liberation_afrf",
"map": "Takistan",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 8,
"KP_liberation_preset_opfor": 3,
"KP_liberation_preset_resistance": 3,
"KP_liberation_preset_civilians": 2,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.Sara",
"missionName": "kp_liberation",
"map": "Sara",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 6,
"KP_liberation_preset_opfor": 2,
"KP_liberation_preset_resistance": 0,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.xcam_taunus",
"missionName": "kp_liberation",
"map": "xcam_taunus",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 4,
"KP_liberation_preset_opfor": 2,
"KP_liberation_preset_resistance": 0,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.pja310",
"missionName": "kp_liberation",
"map": "pja310",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 6,
"KP_liberation_preset_opfor": 2,
"KP_liberation_preset_resistance": 0,
"KP_liberation_preset_civilians": 0,
"KP_liberation_arsenal": 0
}
},
{
"sourceFolder": "kp_liberation.lythium",
"missionName": "kp_liberation",
"map": "lythium",
"configFile": "kp_liberation_config.sqf",
"variables": {
"KP_liberation_preset_blufor": 7,
"KP_liberation_preset_opfor": 2,
"KP_liberation_preset_resistance": 3,
"KP_liberation_preset_civilians": 2,
"KP_liberation_arsenal": 0
}
}
]

145
_tools/gulpfile.ts Normal file
View File

@ -0,0 +1,145 @@
import * as gulp from "gulp";
import * as gulpReplace from "gulp-replace";
import * as gulpPbo from "gulp-armapbo";
import * as gulpZip from "gulp-zip";
import * as vinylPaths from "vinyl-paths";
import * as del from "del";
import { resolve } from "path";
import { MissionPaths } from "./src";
import { Preset, FolderStructureInfo } from "./src";
const presets: Preset[] = require('./_presets.json');
/**
* Mission folders configuration
*/
const paths: FolderStructureInfo = {
frameworkFolder: resolve('..', 'Missionframework'),
missionsFolder: resolve('..', 'Missionbasefiles'),
workDir: resolve("./build")
};
/**
* Create gulp tasks
*/
let taskNames: string[] = [];
let taskNamesPbo: string[] = [];
let taskNamesZip: string[] = [];
for (let preset of presets) {
const mission = new MissionPaths(preset, paths);
const taskName = [preset.missionName, preset.map].join('.');
/**
* Copy mission framework to output dir
*/
taskNames.push('framework_' + taskName);
gulp.task('framework_' + taskName, () => {
return gulp.src(mission.getFrameworkPath().concat('/**/*'))
.pipe(gulp.dest(mission.getOutputDir()));
});
/**
* Copy mission.sqm to output dir
*/
taskNames.push('sqm_' + taskName);
gulp.task('sqm_' + taskName, () => {
return gulp.src(mission.getMissionSqmPath())
.pipe(gulp.dest(mission.getOutputDir()));
});
/**
* Replace variables values in configuration file
*/
taskNames.push('vars_' + taskName);
gulp.task('vars_' + taskName, () => {
let src = gulp.src(mission.getMissionConfigFilePath());
const variables = Object.getOwnPropertyNames(preset.variables);
for (let variable of variables) {
// https://regex101.com/r/YknC8r/1
const regex = new RegExp(`(${variable} += +)(?:\\d+|".+")`, 'ig');
const value = JSON.stringify(preset.variables[variable]);
// replace variable value
src = src.pipe(gulpReplace(regex, `$1${value}`));
}
return src.pipe(gulp.dest(mission.getOutputDir()));
});
/**
* Pack PBOs
*/
taskNamesPbo.push('pack_' + taskName);
gulp.task('pack_' + taskName, () => {
return gulp.src(mission.getOutputDir() + '/**/*')
.pipe(gulpPbo({
fileName: mission.getFullName() + '.pbo',
progress: false,
verbose: false,
// Do not compress (SLOW)
compress: true ? [] : [
'**/*.sqf',
'mission.sqm',
'description.ext'
]
}))
.pipe(gulp.dest(mission.getWorkDir() + '/pbo'));
});
/**
* Create ZIP files
*/
taskNamesZip.push('zip_' + taskName);
gulp.task('zip_' + taskName, () => {
return gulp.src([
resolve('..', './userconfig/**/*'),
resolve('..', 'LICENSE.md'),
resolve('..', 'README.md')
], {
base: resolve('..') // Change base dir to have correct relative paths in ZIP
})
.pipe(
gulp.src(
resolve(mission.getWorkDir(), 'pbo', mission.getFullName() + '.pbo'), {
base: resolve(mission.getWorkDir(), 'pbo') // Change base dir to have correct relative paths in ZIP
})
)
.pipe(gulpZip(
mission.getFullName() + '.zip'
))
.pipe(gulp.dest(mission.getWorkDir()))
});
}
// Main tasks
gulp.task('clean', () => {
return gulp.src(paths.workDir)
.pipe(vinylPaths(del));
});
gulp.task('build', gulp.series(taskNames));
gulp.task('pbo', gulp.series(taskNamesPbo));
gulp.task('zip', gulp.series(taskNamesZip));
gulp.task('default',
gulp.series(
gulp.task('build'),
gulp.task('pbo'),
gulp.task('zip'),
)
);

27
_tools/index.ts Normal file
View File

@ -0,0 +1,27 @@
import { resolve } from "path";
import { Preset, FolderStructureInfo } from "./src";
import { BuilderConfiguration } from "./src";
import { buildMission } from "./src";
const presets: Preset[] = require('./_presets.json');
const builderConf: BuilderConfiguration = {
outputDir: resolve("../build"),
async: false,
verbose: true,
zip: true,
pbo: true
};
const paths: FolderStructureInfo = {
frameworkFolder: resolve('..', 'Missionframework'),
missionsFolder: resolve('..', 'Missionbasefiles', 'kp_liberation.'),
missionConfigFile: 'kp_liberation_config.sqf',
workDir: resolve(builderConf.outputDir)
};
buildMission(presets, paths, builderConf);
console.log();

4869
_tools/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
_tools/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "kp_liberation",
"main": "dist/index.js",
"engines": {
"node": ">=7"
},
"dependencies": {
"@types/del": "^3.0.0",
"@types/gulp": "^4.0.5",
"@types/gulp-replace": "0.0.31",
"@types/gulp-zip": "^4.0.0",
"@types/vinyl-paths": "0.0.31",
"del": "^3.0.0",
"gulp": "^4.0.0",
"gulp-armapbo": "^1.1.3",
"gulp-replace": "^0.6.1",
"gulp-zip": "^4.1.0",
"smart-zip": "0.0.9",
"ts-node": "^4.1.0",
"typescript": "^2.7.1",
"vinyl-paths": "^2.1.0"
},
"scripts": {
"build": "npx gulp"
}
}

56
_tools/src/Config.ts Normal file
View File

@ -0,0 +1,56 @@
export interface Preset {
/**
* Path to folder with mission.sqm relative to "missionsFolder".
* If mission.sqm is in root of "missionsFolder" should be empty string.
*
* @see FolderStructureInfo.missionsFolder
*/
readonly sourceFolder: string;
/**
* Path to file with mission configuration.
* Replacement of variables will be applied here.
*/
readonly configFile: string;
/**
* Name of mission (part before mapname)
*/
readonly missionName: string;
/**
* Map name
*/
readonly map: string;
/**
* key=>val of values to replace in config file
* @see {VariablesReplacements}
*/
readonly variables: VariablesReplacements;
}
export interface VariablesReplacements {
/** Key should be name of variable as set in SQF file, its value will be replaced with one from entry. */
readonly [key: string]: any;
}
export interface FolderStructureInfo {
/**
* Folder of folders with mission.sqm.
* Value of "sourceFolder" from Preset will be appended to this path.
*
* @see {Preset}
*/
readonly missionsFolder: string;
/**
* Path to folder with mission framework files.
*/
readonly frameworkFolder: string;
/**
* Directory containing built missions
*/
readonly workDir: string;
}

View File

@ -0,0 +1,72 @@
import { Preset, FolderStructureInfo } from './Config';
import * as path from "path";
export class MissionPaths {
static readonly missionSQM = 'mission.sqm';
private preset: Preset;
private folderStructure: FolderStructureInfo;
constructor(preset: Preset, folderStructure: FolderStructureInfo) {
this.preset = preset;
this.folderStructure = folderStructure;
}
public getMap(): string {
return this.preset.map;
}
public getName(): string {
return this.preset.missionName;
}
public getFullName(): string {
return [this.getName(), this.getMap()].join('.');
}
public getWorkDir(): string {
return this.folderStructure.workDir;
}
/**
* Get path to source mission.sqm file
*/
public getMissionSqmPath(): string {
return path.resolve(
this.folderStructure.missionsFolder,
this.preset.sourceFolder,
'mission.sqm'
);
}
/**
* Get path to folder with mission framework files.
*/
public getFrameworkPath(): string {
return path.resolve(this.folderStructure.frameworkFolder);
}
/**
* Get path to folder containing mission files
*/
public getOutputDir(): string {
return path.resolve(
this.folderStructure.workDir,
this.getFullName()
);
}
/**
* Get path to file with mission configuration.
* As defined in preset.
*/
public getMissionConfigFilePath(): string {
return path.resolve(
this.getOutputDir(),
this.preset.configFile
);
}
}

4
_tools/src/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from "./MissionPaths";
export * from "./Config";

15
_tools/tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"strictNullChecks": true,
"module": "commonjs",
"target": "es6",
"noUnusedParameters": true,
"noUnusedLocals": true,
"noImplicitAny": true
},
"include": [
"./src/"
]
}