Uploading your code with grunt is just the beginning. This guide covers numerous enhancements that can be added to your scripts to really get the most out of grunt and make your development easier.
Assumptions
This article makes a few assumptions-
- Code is stored in the
srcdirectory. - You've read the Getting Started guide.
- Specifically, you understand how tasks are stored using grunt.initConfig.
Secure Credentials
As your Gruntfile gets more complex you are going to want to save it in source control, but at the same time saving credentials in repositories is generally considered a horrible idea. Moving the credentials to a separate file will allow you to commit your Gruntfile without your credentials.
First create the file .screeps.json to save your credentials.
{
"email": "<YOUR EMAIL HERE>",
"password": "<YOUR PASSWORD HERE>",
"branch": "default",
"ptr": false
}Now modify Gruntfile.js to use the new file.
module.exports = function(grunt) {
var config = require('./.screeps.json')
grunt.loadNpmTasks('grunt-screeps');
grunt.initConfig({
screeps: {
options: {
email: config.email,
password: config.password,
branch: config.branch,
ptr: config.ptr
},
dist: {
src: ['src/*.js']
}
}
});
}Finally, open your .gitignore (create it if it doesn't exist already) and add .screeps.json
/.screeps.jsonOverride Options with CLI Flags
Changing the options that Grunt is using should not require a change in code and can be done using command line flags.
Now update Gruntfile.js so it utilizes the .screeps.json file created above and the grunt.option function.
module.exports = function(grunt) {
var config = require('./.screeps.json')
var branch = grunt.option('branch') || config.branch;
var email = grunt.option('email') || config.email;
var password = grunt.option('password') || config.password;
var ptr = grunt.option('ptr') ? true : config.ptr
grunt.loadNpmTasks('grunt-screeps');
grunt.initConfig({
screeps: {
options: {
email: email,
password: password,
branch: branch,
ptr: ptr
},
dist: {
src: ['src/*.js']
}
}
});
}Now you can override any of the commands on the fly. Any new option can be added to .screeps.json.
grunt screeps --ptr=true --branch=developmentUsing Folders
A common frustration amongst new players is that folders are not available by default. This can be changed using Grunt.
To get started install the grunt-contrib-copy and grunt-contrib-clean plugins.
npm install grunt-contrib-clean --save-dev
npm install grunt-contrib-copy --save-devIn this case the "copy" plugin is going to be used to move code from the src directory to dist. The plugin as an option to rename files, so a function to convert directory delimiters (slashes) to underscores is used to flatten the file structure. Once run the results will look like this-
| Before | After | Require |
|---|---|---|
| src/main.js | dist/main.js | require('main'); |
| src/lib/creeptalk.js | dist/lib_creeptalk.js | require('lib_creeptalk'); |
| src/lib/creeptalk/emoji.js | dist/lib_creeptalk_emoji.js | require('lib_creeptalk_emoji'); |
| src/prototypes/creeps.js | dist/prototypes_creeps.js | require('prototypes_creeps'); |
| src/prototypes/spawns.js | dist/prototypes_spawns.js | require('prototypes_spawns'); |
The copy plugin does not clear any data before it is run, so the clean plugin is used to make sure the dist folder is empty before files are moved into it.
Finally we use grunt.registerTask() to combine these three seperate tasks into one, which we will make the default.
module.exports = function(grunt) {
var config = require('./.screeps.json')
var branch = grunt.option('branch') || config.branch;
var email = grunt.option('email') || config.email;
var password = grunt.option('password') || config.password;
var ptr = grunt.option('ptr') ? true : config.ptr
grunt.loadNpmTasks('grunt-screeps')
grunt.loadNpmTasks('grunt-contrib-clean')
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.initConfig({
screeps: {
options: {
email: email,
password: password,
branch: branch,
ptr: ptr
},
dist: {
src: ['dist/*.js']
}
},
// Remove all files from the dist folder.
clean: {
'dist': ['dist']
},
// Copy all source files into the dist folder, flattening the folder structure by converting path delimiters to underscores
copy: {
// Pushes the game code to the dist folder so it can be modified before being send to the screeps server.
screeps: {
files: [{
expand: true,
cwd: 'src/',
src: '**',
dest: 'dist/',
filter: 'isFile',
rename: function (dest, src) {
// Change the path name utilize underscores for folders
return dest + src.replace(/\//g,'_');
}
}],
}
},
})
grunt.registerTask('default', ['clean', 'copy:screeps', 'screeps']);
}Now with a single command your code will be copied from its src directory, flattened, and then pushed to the screeps server.
gruntAutomatic Versioning
Install the file-append plugin.
npm install grunt-file-append --save-devIn your source code create an empty file named version.js. Grunt is going to use this file to add the global variable SCRIPT_VERSION with the timestamp as its value. Then populate a variable with the current date and create a new file_append task.
module.exports = function(grunt) {
// parameters
grunt.loadNpmTasks('grunt-screeps')
grunt.loadNpmTasks('grunt-contrib-clean')
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.loadNpmTasks('grunt-file-append')
var currentdate = new Date();
// Output the current date and branch.
grunt.log.subhead('Task Start: ' + currentdate.toLocaleString())
grunt.log.writeln('Branch: ' + branch)
grunt.initConfig({
// Truncated for space.
screeps: {},
clean: {},
copy: {},
// Add version variable using current timestamp.
file_append: {
versioning: {
files: [
{
append: "\nglobal.SCRIPT_VERSION = "+ currentdate.getTime() + "\n",
input: 'dist/version.js',
}
]
}
},
})
grunt.registerTask('default', ['clean', 'copy:screeps', 'file_append:versioning', 'screeps']);
}Now by adding require('version') the variable SCRIPT_VERSION will be available. Comparing this to a version string saved in memory allows players to see when new scripts are updated.
require('version')
if(!Memory.SCRIPT_VERSION || Memory.SCRIPT_VERSION != SCRIPT_VERSION) {
Memory.SCRIPT_VERSION = SCRIPT_VERSION
console.log('New code uplodated')
}Private Server
There are two ways to upload code to your private server account using Grunt.
Via Steam Client
The Steam client is used to upload code from your local folder. In this case Grunt can be used to copy the files from the dist folder to the local folder used by steam to upload the data.
Unfortunately the copy plugin can cause some issues with the steam client, so in this case the rsync plugin should be used.
npm install grunt-rsync --save-dev Now add a parameter for private_directory to your settings and grunt files and configure rsync. To make it cross compatible with the main server a seprate private task is created using grunt.registerTask.
module.exports = function(grunt) {
// parameters
var private_directory = grunt.option('private_directory') || config.private_directory;
grunt.loadNpmTasks('grunt-screeps')
grunt.loadNpmTasks('grunt-contrib-clean')
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.loadNpmTasks('grunt-rsync')
var currentdate = new Date();
grunt.initConfig({
// Truncated for space.
screeps: {},
clean: {},
copy: {},
// Copy files to the folder the client uses to sink to the private server.
// Use rsync so the client only uploads the changed files.
rsync: {
options: {
args: ["--verbose", "--checksum"],
exclude: [".git*"],
recursive: true
},
private: {
options: {
src: './dist/',
dest: private_directory,
}
},
},
})
grunt.registerTask('default', ['clean', 'copy:screeps', 'file_append:versioning', 'screeps']);
grunt.registerTask('private', ['clean', 'copy:screeps', 'file_append:versioning', 'rsync:private']);
}Now code can be pushed to your private server.
grunt privateUsing Server Mod
You need to install some authentication mod like screepsmod-auth at your private server in order for this method to work.
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-screeps');
grunt.initConfig({
screeps: {
options: {
server: {
host: 'your.server.hostname.or.ip',
port: 21025,
http: true
},
email: 'YOUR_EMAIL',
password: 'YOUR_PASSWORD',
branch: 'default',
ptr: false
},
dist: {
src: ['dist/*.js']
}
}
});
}Beautify
Keeping code pretty is a common task with Grunt and can be accomplished with the jsbeautifier plugin.
npm install grunt-jsbeautifier --save-devNow add two new tasks for Grunt- one to cleanup the code and one to verify code standards as the start of a test suite (this task can be expanded later). The task is configured to look for .jsbeautifyrc for style rules.
module.exports = function(grunt) {
// parameters
var private_directory = grunt.option('private_directory') || config.private_directory;
grunt.loadNpmTasks('grunt-screeps')
grunt.loadNpmTasks('grunt-contrib-clean')
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.loadNpmTasks('grunt-rsync')
var currentdate = new Date();
grunt.initConfig({
// Truncated for space.
screeps: {},
clean: {},
copy: {},
// Apply code styling
jsbeautifier: {
modify: {
src: ["src/**/*.js"],
options: {
config: '.jsbeautifyrc'
}
},
verify: {
src: ["src/**/*.js"],
options: {
mode: 'VERIFY_ONLY',
config: '.jsbeautifyrc'
}
}
}
})
grunt.registerTask('default', ['clean', 'copy:screeps', 'file_append:versioning', 'screeps']);
grunt.registerTask('private', ['clean', 'copy:screeps', 'file_append:versioning', 'rsync:private']);
grunt.registerTask('test', ['jsbeautifier:verify']);
grunt.registerTask('pretty', ['jsbeautifier:modify']);
}Now code can be altered in place to match rules
grunt prettyor simply tested.
grunt testAdd Stats
Sometimes it gets boring watching your script upload. The plugin time-grunt provides a breakdown of how much time is spent on each task.
npm install time-grunt --save-devNow as the very first line in the grunt function load the special plugin and pass the grunt object to it.
module.exports = function(grunt) {
require('time-grunt')(grunt);
...
}Full Example
Putting it all together gives a powerful but simple to use tool for managing your Screeps deployment.
module.exports = function (grunt) {
require('time-grunt')(grunt);
// Pull defaults (including username and password) from .screeps.json
var config = require('./.screeps.json')
// Allow grunt options to override default configuration
var branch = grunt.option('branch') || config.branch;
var email = grunt.option('email') || config.email;
var password = grunt.option('password') || config.password;
var ptr = grunt.option('ptr') ? true : config.ptr
var private_directory = grunt.option('private_directory') || config.private_directory;
var currentdate = new Date();
grunt.log.subhead('Task Start: ' + currentdate.toLocaleString())
grunt.log.writeln('Branch: ' + branch)
// Load needed tasks
grunt.loadNpmTasks('grunt-screeps')
grunt.loadNpmTasks('grunt-contrib-clean')
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.loadNpmTasks('grunt-file-append')
grunt.loadNpmTasks("grunt-jsbeautifier")
grunt.loadNpmTasks("grunt-rsync")
grunt.initConfig({
// Push all files in the dist folder to screeps. What is in the dist folder
// and gets sent will depend on the tasks used.
screeps: {
options: {
email: email,
password: password,
branch: branch,
ptr: ptr
},
dist: {
src: ['dist/*.js']
}
},
// Copy all source files into the dist folder, flattening the folder
// structure by converting path delimiters to underscores
copy: {
// Pushes the game code to the dist folder so it can be modified before
// being send to the screeps server.
screeps: {
files: [{
expand: true,
cwd: 'src/',
src: '**',
dest: 'dist/',
filter: 'isFile',
rename: function (dest, src) {
// Change the path name utilize underscores for folders
return dest + src.replace(/\//g,'_');
}
}],
}
},
// Copy files to the folder the client uses to sink to the private server.
// Use rsync so the client only uploads the changed files.
rsync: {
options: {
args: ["--verbose", "--checksum"],
exclude: [".git*"],
recursive: true
},
private: {
options: {
src: './dist/',
dest: private_directory,
}
},
},
// Add version variable using current timestamp.
file_append: {
versioning: {
files: [
{
append: "\nglobal.SCRIPT_VERSION = "+ currentdate.getTime() + "\n",
input: 'dist/version.js',
}
]
}
},
// Remove all files from the dist folder.
clean: {
'dist': ['dist']
},
// Apply code styling
jsbeautifier: {
modify: {
src: ["src/**/*.js"],
options: {
config: '.jsbeautifyrc'
}
},
verify: {
src: ["src/**/*.js"],
options: {
mode: 'VERIFY_ONLY',
config: '.jsbeautifyrc'
}
}
}
})
// Combine the above into a default task
grunt.registerTask('default', ['clean', 'copy:screeps', 'file_append:versioning', 'screeps']);
grunt.registerTask('private', ['clean', 'copy:screeps', 'file_append:versioning', 'rsync:private']);
grunt.registerTask('test', ['jsbeautifier:verify']);
grunt.registerTask('pretty', ['jsbeautifier:modify']);
}