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
src
directory. - 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.json
Override 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=development
Using 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-dev
In 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.
grunt
Automatic Versioning
Install the file-append plugin.
npm install grunt-file-append --save-dev
In 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 private
Using 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-dev
Now 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 pretty
or simply tested.
grunt test
Add 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-dev
Now 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']);
}