diff --git a/TODO.md b/TODO.md index b138d93..81cc7df 100644 --- a/TODO.md +++ b/TODO.md @@ -103,8 +103,6 @@ Common UI Technical --------- -* Fix "npm test" returning 0 even on failure -* Fix "npm start" stopping when there is an error in initial build * Pack sounds * Add toggles for shaders, automatically disable them if too slow, and initially disable them on mobile diff --git a/activate_node b/activate_node index 5548774..27d88e6 100644 --- a/activate_node +++ b/activate_node @@ -10,5 +10,5 @@ fi vdir="./.venv" test -x "${vdir}/bin/nodeenv" || ( virtualenv -p python3 "${vdir}" && "${vdir}/bin/pip" install --upgrade nodeenv ) -test -e "${vdir}/node/bin/activate" || "${vdir}/bin/nodeenv" --node=9.3.0 --force "${vdir}/node" +test -e "${vdir}/node/bin/activate" || "${vdir}/bin/nodeenv" --node=9.11.1 --force "${vdir}/node" source "${vdir}/node/bin/activate" diff --git a/package.json b/package.json index 44c3fcf..77810ba 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "out/build.js", "scripts": { "build": "run build", - "test": "run test", + "test": "run ci", "start": "run continuous", "codecov": "remap-istanbul -i out/coverage/coverage.json -o out/coverage/mapped.json -t json && codecov -f out/coverage/mapped.json", "deploy": "run deploy" @@ -41,4 +41,4 @@ "parse": "^1.11.0", "phaser": "2.6.2" } -} +} \ No newline at end of file diff --git a/runfile.js b/runfile.js index 1b6aac4..b606d50 100644 --- a/runfile.js +++ b/runfile.js @@ -14,6 +14,8 @@ String.prototype.rsplit = function (sep, maxsplit) { return maxsplit ? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit)) : split; } +let forever = () => new Promise(() => null); + /** * Promise-compatible version of glob */ @@ -26,141 +28,139 @@ function list(pattern) { /** * Asynchronous execution of shell commands */ -function exec(command) { - return run(command, { async: true }); +async function exec(command) { + await run(command, { async: true }); } /** * Build app from typescript sources */ -function ts(dist = false) { +async function ts(dist = false) { console.log("Building app..."); - return exec(`tsc -p ${dist ? "./tsconfig.dist.json" : "."}`); + await exec(`tsc -p ${dist ? "./tsconfig.dist.json" : "."}`); } /** * Start watching for typescript changes */ -function watch_ts() { +async function watch_ts() { watch(["./src/**/*.ts", "package.json"], () => ts()); + await forever(); } /** * Build an atlas of images for a given stage */ -function atlas(stage) { +async function atlas(stage) { shell.rm('-f', `out/assets/atlas${stage}-*`); - return list(`data/stage${stage}/image/**/*.{png,jpg}`).then(files => { - let opts = { - name: `out/assets/atlas${stage}`, - fullpath: true, - width: 2048, - height: 2048, - square: true, - powerOfTwo: true, - padding: 2 - }; - return new Promise(resolve => gfPacker(files, opts, resolve)); - }).then(() => { - return list(`out/assets/atlas${stage}-*.json`); - }).then(outfiles => { - return Promise.resolve(outfiles.map(file => path.basename(file).replace('.json', ''))) - }); + + let files = await list(`data/stage${stage}/image/**/*.{png,jpg}`); + + let opts = { + name: `out/assets/atlas${stage}`, + fullpath: true, + width: 2048, + height: 2048, + square: true, + powerOfTwo: true, + padding: 2 + }; + await new Promise(resolve => gfPacker(files, opts, resolve)); + let outfiles = await list(`out/assets/atlas${stage}-*.json`); + return outfiles.map(file => path.basename(file).replace('.json', '')); } /** * Build a single data pack */ -function pack(stage) { +async function pack(stage) { console.log(`Building pack ${stage}...`); let getKey = x => x.replace(/\//g, "-").replace(/\.[a-z0-9]+$/, ''); - return atlas(stage).then(files => { - return Promise.resolve(files.map(file => { - let fname = path.basename(file); - return { - type: "atlasJSONHash", - key: fname, - atlasURL: `assets/${fname}.json?t=${Date.now()}`, - textureURL: `assets/${fname}.png`, - atlasData: null - } - })); - }).then(items => { - return list(`data/stage${stage}/sound/**/*.{mp3,wav}`).then(files => { - return Promise.resolve(items.concat(files.map(file => { - const [name, ext] = file.rsplit('.'); - const key = getKey(name.replace(`data/stage${stage}/sound/`, '')); - shell.cp(file, `out/assets/${key}.${ext}`); - return { - type: "audio", - key: key, - urls: [`assets/${key}.${ext}?t=${Date.now()}`], - autoDecode: (ext == 'mp3') - }; - }))); - }); - }).then(items => { - return list(`data/stage${stage}/shader/**/*.glsl`).then(files => { - return Promise.resolve(items.concat(files.map(file => { - const [name, ext] = file.rsplit('.'); - const key = getKey(name.replace(`data/stage${stage}/shader/`, '')); - shell.cp(file, `out/assets/${key}.${ext}`); - return { - type: "shader", - key: key, - url: `assets/${key}.${ext}?t=${Date.now()}` - }; - }))); - }); - }).then(items => { - let packdata = {}; - packdata[`stage${stage}`] = items; - return new Promise(resolve => fs.writeFile(`out/assets/pack${stage}.json`, JSON.stringify(packdata), 'utf8', resolve)); + let files = await atlas(stage) + let items = files.map(file => { + let fname = path.basename(file); + return { + type: "atlasJSONHash", + key: fname, + atlasURL: `assets/${fname}.json?t=${Date.now()}`, + textureURL: `assets/${fname}.png`, + atlasData: null + } }); + + files = await list(`data/stage${stage}/sound/**/*.{mp3,wav}`); + items = items.concat(files.map(file => { + const [name, ext] = file.rsplit('.'); + const key = getKey(name.replace(`data/stage${stage}/sound/`, '')); + shell.cp(file, `out/assets/${key}.${ext}`); + return { + type: "audio", + key: key, + urls: [`assets/${key}.${ext}?t=${Date.now()}`], + autoDecode: (ext == 'mp3') + }; + })); + + files = await list(`data/stage${stage}/shader/**/*.glsl`); + items = items.concat(files.map(file => { + const [name, ext] = file.rsplit('.'); + const key = getKey(name.replace(`data/stage${stage}/shader/`, '')); + shell.cp(file, `out/assets/${key}.${ext}`); + return { + type: "shader", + key: key, + url: `assets/${key}.${ext}?t=${Date.now()}` + }; + })); + + let packdata = {}; + packdata[`stage${stage}`] = items; + await new Promise(resolve => fs.writeFile(`out/assets/pack${stage}.json`, JSON.stringify(packdata), 'utf8', resolve)); } /** * Build data packs */ -function data() { +async function data() { shell.mkdir("-p", "out/assets"); - return Promise.all([1, 2, 3].map(stage => pack(stage))); + await Promise.all([1, 2, 3].map(stage => pack(stage))); } /** * Start watch for data changes */ -function watch_data() { +async function watch_data() { watch(["./data/**/*.*"], () => data()); + await forever(); } /** * Copy the vendors from node_modules to dist directory */ -function vendors() { +async function vendors() { console.log("Copying vendors..."); shell.rm('-rf', 'out/vendor'); shell.mkdir('-p', 'out/vendor'); shell.cp('-R', 'node_modules/phaser/build', 'out/vendor/phaser'); shell.cp('-R', 'node_modules/parse/dist', 'out/vendor/parse'); shell.cp('-R', 'node_modules/jasmine-core/lib/jasmine-core', 'out/vendor/jasmine'); - return Promise.resolve(); } /** * Start watching for vendors changes */ -function watch_vendors() { +async function watch_vendors() { watch(['package.json'], () => vendors()); + await forever(); } /** * Trigger a single build */ -function build(dist = false) { - return Promise.all([ +async function build(dist = false) { + await Promise.all([ ts(dist), data(), vendors() @@ -170,35 +170,47 @@ function build(dist = false) { /** * Optimize the build for production */ -function optimize() { +async function optimize() { // TODO do not overwrite dev build - return exec("uglifyjs out/build.dist.js --source-map --output out/build.js"); + await exec("uglifyjs out/build.dist.js --source-map --output out/build.js"); } /** * Deploy to production */ -function deploy(task) { - return build(true).then(optimize).then(() => { - return exec("rsync -avz --delete ./out/ hosting.thunderk.net:/srv/website/spacetac/") - }); +async function deploy(task) { + await build(true); + await optimize(); + await exec("rsync -avz --delete ./out/ hosting.thunderk.net:/srv/website/spacetac/"); } /** - * Run tests in karma, using freshly built app + * Run tests in karma */ -function test(task) { - return Promise.all([ts(), vendors()]).then(() => { - return exec("karma start spec/support/karma.conf.js"); - }).then(() => { - return exec("remap-istanbul -i out/coverage/coverage.json -o out/coverage -t html"); - }); +async function karma() { + await exec("karma start spec/support/karma.conf.js"); +} + +/** + * Run tests in karma (suppose is already built) + */ +async function test(task) { + await karma(); +} + +/** + * Run tests in karma, using freshly built app (for continuous integration) + */ +async function ci(task) { + await Promise.all([ts(), vendors()]); + await karma(); + await exec("remap-istanbul -i out/coverage/coverage.json -o out/coverage -t html"); } /** * Serve the app for dev purpose */ -function serve() { +async function serve() { liveServer.start({ host: '127.0.0.1', port: 8012, @@ -206,31 +218,52 @@ function serve() { ignore: 'coverage', wait: 500 }); - return new Promise(() => null); + await new Promise(() => null); } /** * Continuous development server */ -function continuous() { - return build().then(() => Promise.all([ +async function continuous() { + try { + await build(); + } catch (err) { + console.error(err); + } + + await Promise.all([ serve(), watch_ts(), watch_data(), watch_vendors() - ])); + ]); +} + +/** + * Wrapper around an async execution function, to make it a runjs command + */ +function command(func) { + return async function () { + try { + await func(); + } catch (err) { + console.error(err); + process.exit(1); + } + } } module.exports = { - ts, - watch_ts, - data, - watch_data, - vendors, - watch_vendors, - build, - test, - deploy, - serve, - continuous + ts: command(ts), + watch_ts: command(watch_ts), + data: command(data), + watch_data: command(watch_data), + vendors: command(vendors), + watch_vendors: command(watch_vendors), + build: command(build), + test: command(test), + deploy: command(deploy), + serve: command(serve), + continuous: command(continuous), + ci: command(ci) } diff --git a/src/core/actions/VigilanceAction.spec.ts b/src/core/actions/VigilanceAction.spec.ts index 341148d..1577239 100644 --- a/src/core/actions/VigilanceAction.spec.ts +++ b/src/core/actions/VigilanceAction.spec.ts @@ -24,25 +24,25 @@ module TK.SpaceTac.Specs { intruder_count: 0, intruder_effects: [new ValueEffect("hull", -1)] }); - check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 for all incoming ships"); + check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 on all incoming ships"); action = new VigilanceAction("Reactive Fire", { power: 2, radius: 120 }, { intruder_count: 1, intruder_effects: [new ValueEffect("hull", -1)] }); - check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 for the first incoming ship"); + check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 on the first incoming ship"); action = new VigilanceAction("Reactive Fire", { power: 2, radius: 120 }, { intruder_count: 3, intruder_effects: [new ValueEffect("hull", -1)] }); - check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 for the first 3 incoming ships"); + check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 on the first 3 incoming ships"); action = new VigilanceAction("Reactive Fire", { power: 2, radius: 120, filter: ActionTargettingFilter.ALLIES }, { intruder_count: 3, intruder_effects: [new ValueEffect("hull", -1)] }); - check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 for the first 3 incoming team members"); + check.equals(action.getEffectsDescription(), "Watch a 120km area (power usage 2):\n• hull -1 on the first 3 incoming team members"); }); test.case("handles the vigilance effect to know who to target", check => { diff --git a/src/core/actions/VigilanceAction.ts b/src/core/actions/VigilanceAction.ts index fd6e69b..ffaccd9 100644 --- a/src/core/actions/VigilanceAction.ts +++ b/src/core/actions/VigilanceAction.ts @@ -49,11 +49,11 @@ module TK.SpaceTac { let suffix: string; if (this.intruder_count == 0) { - suffix = `for all incoming ${BaseAction.getFilterDesc(this.filter)}`; + suffix = `on all incoming ${BaseAction.getFilterDesc(this.filter)}`; } else if (this.intruder_count == 1) { - suffix = `for the first incoming ${BaseAction.getFilterDesc(this.filter, false)}`; + suffix = `on the first incoming ${BaseAction.getFilterDesc(this.filter, false)}`; } else { - suffix = `for the first ${this.intruder_count} incoming ${BaseAction.getFilterDesc(this.filter)}`; + suffix = `on the first ${this.intruder_count} incoming ${BaseAction.getFilterDesc(this.filter)}`; } let effects = this.intruder_effects.map(effect => {